우리는 객체지향 프로그래밍을 하면서 많은 클래스를 만들고, 이를 통해 다양한 객체들을 만들어 상호작용 시킨다.
이 때, 서로 비슷한 클래스거나, 서로 공유하는 부분이 많은 클래스들이 생기는 경우가 있다.
자동차를 예로 들어보자.
자동차의 종류는 여러가지이다. SUV도 있고, 트럭도 있고, 승용차도 있다.
이 자동차들은 모두 바퀴가 달려있으며, 엔진이 있고, 시동거는 기능, 엑셀 기능 등 여러 기능과 요소들이 공통되어있다.
이런 경우에, 우리는 아래와 같이 하나하나 전부 다른 클래스들로 만들었어야 했다.
using System;
namespace Favor
{
class SUV
{
int wheel;
string engine;
float oil;
void Accel() { Console.WriteLine("SUV 가속"); }
}
class Truck
{
int wheel;
string engine;
float oil;
void Load() { Console.WriteLine("트럭에 짐 적재"); }
}
class Bus
{
int wheel;
string engine;
float oil;
void DoorOpen() { Console.WriteLine("문이 열립니다."); }
}
//...
}
공통되는 점이 많음에도 불구하고, 서로 다른 기능이 있으면 완전히 복사할 수 없으니 위 처럼 하나하나 다르게 클래스로 선언하여 객체로 인스턴스 했어야 했다.
그렇다면 공통되는 부분은 그대로 가질 수 있되, 그 안에서 서로 다른 특징들만 따로 만드는 방법은 없을까?
이를 위해 등장한 개념인 클래스의 상속이다.
클래스 상속은 하나의 클래스가 갖고있는 모든 멤버를 상속할 클래스에게 물려주는 기능이다.
예시를 위해 위의 자동차 클래스들을 상속해주도록 하겠다.
using System;
namespace Favor
{
class Vehicle
{
int wheel;
string engine;
float oil;
void Break() { Console.WriteLine("정지"); }
}
class SUV : Vehicle
{
void Accel() { Console.WriteLine("SUV 가속"); }
}
class Truck : Vehicle
{
void Load() { Console.WriteLine("트럭에 짐 적재"); }
}
class Bus : Vehicle
{
void DoorOpen() { Console.WriteLine("문이 열립니다."); }
}
//...
}
위의 코드는 Vehicle 클래스를 Suv, Truck, Bus 클래스들이 상속받은 모습이다.
이렇게 상속받은 자식 클래스(파생 클래스라고 불린다.)들은 물려준 부모 클래스의 모든 멤버를 그대로 물려받는다.
즉, 따로 적진 않았어도 자식 클래스들은 각자 wheel, engine, oil 변수를 갖고있는 것이다.
클래스를 상속시키는 방법은 다음과 같다.
상속받을 클래스들의 뒤에 : 를 붙이고, 그 뒤에 상속시켜줄 클래스의 이름을 입력하면 끝이다.
이렇게 모든 멤버들을 상속받은 후에는, 생성자를 통해 부모의 멤버 변수를 스스로에게 맞는 값으로 초기화 시켜주거나, 부모의 멤버 함수를 호출시킬 수 있다.
using System;
namespace Favor
{
class Vehicle
{
int wheel;
string engine;
float oil;
public void Break() { Console.WriteLine("정지"); }
}
class SUV : Vehicle
{
public SUV() { wheel = 4; engine = "SUV엔진"; oil = 62.3; } // 런타임 에러
void Accel() { Console.WriteLine("SUV 가속"); }
}
internal class Program
{
static void Main(string[] args)
{
SUV suv = new SUV();
suv.Break();
// 출력값 : 정지
}
}
}
위 처럼, 부모의 메서드에 접근해 사용하는 것이 가능하다.
그런데 런타임 에러가 발생했다. 자세히 읽어보니, 보호수준 때문에 Vehicle의 wheel과 engine, oil에 접근할 수 없다는 것이다.
분명 자식 클래스는 부모 클래스의 모든 것을 물려받는다고 했는데, 런타임 에러가 출력되는 이유는 무엇일까?
이유는 바로 에러가 친절히 알려주었듯 보호수준 때문이다.
그러면 보호수준은 무엇이며, 어떻게 해야 자식 클래스가 부모 클래스의 것을 온전히 사용할 수 있을까?
힌트는 Break()를 사용했다는 것에 있다.
public은 모든 접근을 허용한다고 했었다. 그렇기 때문에 자식 클래스가 사용이 가능했다.
물론 자식 클래스인 SUV외에도 Vehicle을 통해 인스턴스된 모든 객체를 통한다면, 무엇이든 Break를 사용할 수 있었다.
예전에 class의 기본(default)는 private이라고 설명했던 적이 있다.
위에 Vehicle은 접근제한자를 따로 설정하지 않고 wheel, engine, oil을 선언했기 때문에 현재 private인 상태로, 오직 Vehicle 본인만이 접근할 수 있는 변수인 것이다.
하지만 나는 외부에서는 접근이 불가능하지만, 자식 클래스에게만은 접근할 수 있도록 하고싶다.
이를 위해 우리는 새롭게 배운 접근제한자, protected를 사용할 수 있다.
protected는 자신과, 자신을 상속받은 자식클래스에게만 접근이 허용되는 접근제한자다.
외부에서는 접근할 수 없지만, 자식과 본인은 접근이 가능하다.
이를 이용해, 외부에서 접근이 불가능하지만, 자식 클래스는 접근할 수 있는 변수를 만들 수 있다.
using System;
namespace Favor
{
class Vehicle
{
public void Indicator()
{
Console.WriteLine("{0}, {1}, {2}", wheel, engine, oil);
}
int count;
protected int wheel;
protected string engine;
protected float oil;
}
class SUV : Vehicle
{
public SUV() { wheel = 4; engine = "SUV엔진"; oil = 62.3; }
void Accel() { Console.WriteLine("SUV 가속"); }
void PlusCount() { count++; } // 런타임 에러
}
internal class Program
{
static void Main(string[] args)
{
SUV suv = new SUV();
suv.Indicator();
// 출력값 : 4, SUV엔진, 62.3
}
}
}
접근제한자 protected를 통해, 외부가 아닌 파생클래스 SUV에게만 wheel, oil, engine에 대한 접근이 허용되어 생성자를 통해 값을 초기화 하였다.
그러나 접근제한자를 설정하지 않은 멤버 count는, 아무리 자식 클래스여도 접근이 불가능하다.
위에서 void PlusCount를 통해 count값에 접근하고자 했으나, 런타임 에러. 이번에도 보호수준 때문에 접근이 불가능하다는 에러가 출력됐다.
지금까지 배운 접근 제한자에 대해 정리해보자면,
private : 내부에서만 접근 허용.
public : 내부, 자식 클래스, 외부에서도 접근 허용.
protected : 내부와 자식 클래스에게만 접근 허용.
이와 같이 정리할 수 있겠다.
객체지향의 꽃이라고 부를 수 있는 클래스.
그리고 클래스를 더욱 편리하게 사용할 수 있으며, 서로 다르지만 공통되는 점이 많은 클래스들을 자유롭게 만들 수 있는 상속의 개념을 알아보았다.
공부하면서 개인적으로는 가장 인상적이었고, 매력적인 개념이라고 생각했다.
각각의 개체에게 공통점을 공유해줄 수 있으며, 자식들 사이에서도, 서로 다른 점들을 부각시킬 수 있는 점을 통해, 더 다양하고 많은 기능을 구현할 수 있을 것 같다.
클래스의 상속에는 더 배워야할 점들이 많고, 그렇기에 아직 다루지 않은 내용들이 남아 있으므로 이번 장에서는 상속의 개념에 대해서만 배우고, 클래스를 상속할 때 사용할 수 있는 여러 개념들을
추상 클래스,
가상 클래스,
인터페이스로 이루어진 총 세장으로 나누어 설명하겠다.
'C# 일기' 카테고리의 다른 글
| 10. 추상 클래스(Abstract Class) (2) | 2024.03.06 |
|---|---|
| 9. 가상 함수와 오버라이드 (Virtual & Override) (0) | 2024.03.06 |
| 7. 프로퍼티(Property) (0) | 2024.03.05 |
| 6. 정적 멤버 (static) (0) | 2024.03.05 |
| 5. 얕은 복사(Shadow Copy)와 깊은 복사(Deep Copy) (0) | 2024.03.05 |