본문 바로가기
강좌연구

[C++ 강좌연구] 오버라이딩

by 두들낙서 2019. 2. 2.

다음 세 부분을 어떻게 설명할지 고민이다.


1. 오버라이딩

2. 가상 함수

3. 형변환


이 글에서는 오버라이딩을 먼저 설명해보겠다.

자식 클래스에서는 부모 클래스에서 선언된 것과 동일한 이름의 멤버를 사용할 수 있다. 


1
2
3
4
5
6
7
8
9
class Base {
public:
   int a = 1;
};
 
class Derived : public Base {
public:
   int a = 2;
};
cs

이렇게 써도 에러가 나지 않는다.


그럼 Base와 Derived 타입의 객체를 만들어 a 멤버변수를 출력해보자.


1
2
3
4
5
6
7
int main() {
   Base b;
   Derived d;
 
   cout << b.a << endl;
   cout << d.a << endl;
}
cs


<출력 결과>

1

2


b.a는 1이 출력되는 것이 당연하다.

d.a는 2가 출력되었다. 즉 Base의 a가 아닌 Derived에 있는 a가 출력되었다고 볼 수 있다.


여기서 짚고 넘어가야 할 것은, Derived 클래스에는 실제로 두 개의 멤버변수가 있다는 것이다.

Base의 a를 상속받고, 고유의 a를 또 하나 더 가지고 있는 상태이다. 그러나 d.a라고 하면 Derived 클래스에 정의된 a만 접근 가능하다.


그 이유가 바로 Derived 클래스의 a가 Base 클래스의 a보다 우선하기(override) 때문이다.

이것을 오버라이딩이라 한다.


당연히 그렇다고 해서 Base에 있는 a를 접근할 수 없는 것은 아니다.

예전 강좌에서 클래스 이름은 네임스페이스의 기능도 한다고 얘기한 적이 있다.

즉 네임스페이스 표시(::)를 통해 명시적으로 Base에서 선언된 멤버라는 것을 알려주면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Base {
public:
   int a = 1;
};
 
class Derived : public Base {
public:
   int a = 2;
};
 
int main() {
   Base b;
   Derived d;
 
   cout << d.a << endl;
   cout << d.Base::a << endl;
   cout << d.Derived::a << endl;
}
cs


<출력 결과>

2

1

2



변수뿐만 아니라 함수도 오버라이딩할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base {
public:
   void Print() {
      cout << "Base" << endl;
   }
};
 
class Derived : public Base {
public:
   void Print() {
      cout << "Derived" << endl;
   }
};
 
int main() {
   Base b;
   Derived d;
 
   d.Print();
   d.Base::Print();
   d.Derived::Print();
}

cs


<출력 결과>

Derived

Base

Derived


변수의 오버라이딩을 잘 이해했다면 설명이 필요없는 부분이다.



오버라이딩이 중요해지는 것은 포인터를 사용하면서부터이다.


그보다 먼저 알아야 할 내용이 있다.


1
2
int a = 10;
char *= &a;
cs


에러가 난다.

char타입의 포인터는 char타입의 변수만 가리킬 수 있기 때문이다.


그런데 다음 코드를 보자.

(Derived는 아까처럼 Base를 상속받은 클래스라고 가정한다.)


1
2
Derived d;
Base *= &d;
cs


에러가 나지 않는다!

일반화해서 말하면,

부모를 가리키는 포인터는 자식도 가리킬 수 있다.

두 클래스가 int와 char처럼 완전히 남남 관계는 아니라는 건 알지만, 그래도 신기하다.

물론 Derived 포인터로 Base 객체를 가리키는 건 에러가 난다.


"이게 왜 문법적으로 오류가 없는가?"

사실은 C++를 만든 사람들 마음이다. 원래는 다른 타입끼리는 포인터 참조가 불가한데, 부모-자식 관계에서는 허용할 만한 이유가 있는 것이다.


다시 말해 "이 기능을 왜 만들었는가?"에 대해 생각해보자.


힘들다. 다음번에 이어서 적겠다.

'강좌연구' 카테고리의 다른 글

[C++ 강좌연구] 동적 바인딩  (0) 2019.06.18
[C++ 강좌연구] 정적 바인딩  (0) 2019.02.02

댓글