C++에선 클래스에서 다른 클래스를 상속받아 사용할 수 있고, 상속한 클래스에 이미 정의되어 있는 함수를 재정의하는 오버라이딩이 가능하다.
그리고 상속받은 클래스를 상속한 클래스의 자료형으로 사용하는 업캐스팅이 가능한데, 오버라이딩과 업캐스팅을 둘 다 해주다보면 문제가 발생한다.
class A
{
public:
void f() { cout << "A" << endl; }
};
class B : public A
{
public:
void f() { cout << "B" << endl; }
};
int main()
{
A *a = new B();
B *b = new B();
a->f();
b->f();
}
업캐스팅을 하지 않은 b 변수는 f 함수를 실행하였을때 정상적으로 B 클래스의 f 함수가 실행되어 "B" 를 출력하지만, A 클래스로 업캐스팅 된 a 변수는 B 클래스의 생성자를 이용해 생성해주었음에도 A 클래스의 f 함수가 실행되어 "A"를 출력하게 된다.
이 문제를 해결해주기 위한 것이 virtual 키워드이다.
오버라이딩 할 함수의 부모클래스에 있는 함수에 virtual 을 붙여주면
class A
{
public:
virtual void f() { cout << "A" << endl; }
};
class B : public A
{
public:
void f() { cout << "B" << endl; }
};
업캐스팅한 자식클래스에서도 정상적으로 원래 본인의 함수를 실행해준다.
상속 해줄 클래스를 만들 때 특히 소멸자는 항상 virtual 키워드를 붙여서 선언해주어야 한다.
C++에서는 다중상속을 지원해주는데, 이 경우 다이아몬드 상속에서 문제가 발생한다.
다이아몬드 상속에서는 상속받은 클래스에서 생성자를 호출할 때 상속한 클래스의 생성자를 먼저 호출해주는데, 위 그림에서 B와 C 모두 A를 상속받기 때문에 A 생성자가 두 번 실행되게 된다.
class A
{
public:
A() { cout << "A constructor" << endl; }
};
class B : virtual public A
{
public:
B() { cout << "B constructor" << endl; }
};
class C : virtual public A
{
public:
C() { cout << "C constructor" << endl; }
};
class D : public B, public C
{
public:
D() { cout << "D constructor" << endl; }
};
이를 방지하기 위해 다중상속 받을 클래스 B와 C에서 A를 상속 받을 때 앞에 virtual 키워드를 붙여주면 A 생성자가 중복해서 실행되지 않는다.