C++ Chapter 12.8 : 가상 상속으로 다이아몬드 상속 문제 해결
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!
chapter 12. 가상 함수들 : 가상 상속으로 다이아몬드 상속 문제 해결
🔔 다이아몬드 상속 문제
- B1, B2 클래스는 A 클래스를 상속받고 C 는 B1, B2 클래스를 둘 다 상속 받는다.(다중 상속)
- 1 번 그림을 생각하며 C 에 A 타입의 메모리가 단 하나만 존재 할 것을 기대하지만 실제론 2 번 그림처럼 C 에 A 타입 메모리가 2 개가 생긴다.
다이아몬드 상속 문제 👉 다중 상속시, 다이아몬드 구조로 상속되는 것이 아닌 손자 객체에 할머니 객체가 2 개가 생기는 것.
#include <iostream>
using namespace std;
class PoweredDevice
{
public:
int m_i;
PoweredDevice(int power)
{
cout << "PoweredDevice: " << power << "\n";
}
};
class Scanner : public PoweredDevice
{
public:
Scanner(int scanner, int power)
:PoweredDevice(power)
{
cout << "Scanner: " << scanner << "\n";
}
};
class Printer : public PoweredDevice
{
public:
Printer(int printer, int power)
: PoweredDevice(power)
{
cout << "Printer: " << printer << "\n";
}
};
class Copier : public Scanner, public Printer
{
public:
Copier(int scanner, int printer, int power)
: Scanner(scanner, power), Printer(printer, power)
{}
};
int main()
{
Copier cop(1, 2, 3); //생성자 호출
cout << &cop.Scanner::PoweredDevice::m_i << endl;
cout << &cop.Printer::PoweredDevice::m_i << endl;
return 0;
}
💎출력💎
PoweredDevice : 3
Scanner : 1
Powereddevice : 3
Printer : 2
000000A95D0FF9EC
000000A95D0FF9E8
- 할머니인
PoweredDevice
의 생성자가 2 번 호출되는 것을 볼 수 잇다.- 즉 손자인
Copier
타입의 객체 cop에PoweredDevice
타입의 사본이 2 개가 들어있음을 알 수 있다.
- 즉 손자인
- m_i 는
PoweredDevice
로부터 상속 받은 멤버 변수인데Scanner
로부터 받은 m_i 과Printer
로부터 받은 m_i의 주소가 다른 것을 보아 동일한 하나의PoweredDevice
가 아닌 각각 다른 두개의PoweredDevice
타입 객체가 cop에 상속되었음을 알 수 있다.
🔔 virtual 가상 상속
virtual
로 가상 상속 받으면 상속시 부모 타입의 메모리가 중복되지 않으면서 상속된다. 위 문제 해결!
- 단, 다중 상속 하는 손자 클래스의 경우 할머니 클래스의 생성자를 꼭 직접 호출해주어야 한다.
#include <iostream>
using namespace std;
class PoweredDevice
{
public:
int m_i;
PoweredDevice(int power)
{
cout << "PoweredDevice: " << power << "\n";
}
};
class Scanner : virtual public PoweredDevice
{
public:
Scanner(int scanner, int power)
:PoweredDevice(power)
{
cout << "Scanner: " << scanner << "\n";
}
};
class Printer : virtual public PoweredDevice
{
public:
Printer(int printer, int power)
: PoweredDevice(power)
{
cout << "Printer: " << printer << "\n";
}
};
class Copier : public Scanner, public Printer
{
public:
Copier(int scanner, int printer, int power)
: Scanner(scanner, power), Printer(printer, power), PoweredDevice(power)
{}
};
int main()
{
Copier cop(1, 2, 3); //생성자 호출
cout << &cop.Scanner::PoweredDevice::m_i << endl;
cout << &cop.Printer::PoweredDevice::m_i << endl;
return 0;
}
💎출력💎
PoweredDevice : 3
Scanner : 1
Printer : 2
000000A95D0FF998
000000A95D0FF998
Scanner
,Print
는PoweredDevice
를 가상으로 상속 받고 있다.class Scanner : virtual public PoweredDevice class Copier : public Scanner, public Printer
- 이 덕분에
Scanner
,Print
을 둘 다 상속 받는Copier
에PoweredDevice
타입이 중복되지 않고 단 하나만 존재하게 된다.- 출력 결과
PoweredDevice
타입 생성자가 딱 한번만 호출된 것을 볼 수 있다. Scanner
로부터 받은 m_i 과Printer
로부터 받은 m_i의 주소 또한 동일하다는 것을 알 수 있다.
- 출력 결과
- 이 덕분에
Copier
의 생성자에서 따로 직접PoweredDevice
타입 생성자를 호출해주어야 한다.PoweredDevice
을 가상 상속을 받은 후로 그의 자식인Scanner
와Printer
는 더 위로 올라가지 않기 때문에PoweredDevice
생성자 호출를 직접 해주어야 한다.- 안해주면 컴파일러가 알아서
PoweredDevice
의 디폴트 생성자를 호출하는데 현재PoweredDevice
생성자 중 매개변수가 있는 생성자가 있으므로 이를 직접 호출해야 에러가 안난다.Copier(int scanner, int printer, int power) : Scanner(scanner, power), Printer(printer, power), PoweredDevice(power) {}
- 어차피 가장 조상은
PoweredDevice
이기 때문에 초기화 목록에서 제일 뒤에있어도PoweredDevice
가 가장 먼저 메모리에 정의 된다. 참고 포스트 - 가상 상속의 원리
- 안해주면 컴파일러가 알아서
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글남기기