오늘은 가상상속에 대해서 공부하도록 하겠습니다.
가상 상속이란???
C++에는 다중 상속을 지원하지만 JAVA는 다중 상속을 지원하지 않습니다.
다중상속에는 장점도 있지만 매우 큰 단점이 하나 있습니다.
장점은 객체에 재사용성을 즉 상속성을 좀더 유연하게 만들어 줄수 있습니다.
하지만 단점으로는 다이아몬드 상속 구조를 사용할 경우 매우 큰 성능 저하 및 메모리의 낭비가 올수 있습니다.
아래의 그림을 보시면 다이아 몬드 상속을 대표적으로 보여주고 있습니다.
이러한 상속 구조가 될경우 B나 C는 A를 포함하여 메모리상에 올리며 매우 좋은 상속 구조를 가질수 있지만
D같은 경우는 B 랑 C를 상속을 받지만 결국 B 랑 C는 A를 상속 받고 있기 떄문에 D는 메모리 구조상 A를 두번 할당을 받습니다.
또한, 생성자 및 소멸자를 호출할때는 꼭 부모를 타고 조상 클래스까지 호출을 합니다.
D는 B 와 C를 상속을 받고 있으므로 A의 생성자를 두번 소멸자를 두번 호출하게 됩니다.
이러면 성능적으로 매우 우아하지 못합니다.
코드
#include <iostream>
using namespace std;
class A {
public:
A() { printf("A 생성자\n"); }
~A() { printf("A 소멸자\n"); }
int A_num;
};
class B : public A {
public:
B() { printf("B 생성자\n"); }
~B() { printf("B 소멸자\n"); }
private:
int B_num;
};
class C : public A {
public:
C() { printf("C 생성자\n"); }
~C() { printf("C 소멸자\n"); }
private:
int C_num;
};
class D : public B, public C{
public:
D() { printf("D 생성자\n");
B::A_num = 0;
}
~D() { printf("D 소멸자\n");
}
private:
int D_num;
};
int main(void) {
D d;
return 0;
}
현재 D의 메모리 구조입니다. A를 두개를 받고 있습니다.
또한, A의 변수를 두번 메모리에 올리는 참사가 발생하고 말았습니다.
이를 해결할수 있는 최고의 방법은 Virtual을 사용하여 상속을 하는 방법이 있습니다.
코드
#include <iostream>
class A {
public:
A() { printf("A 생성자\n"); }
~A() { printf("A 소멸자\n"); }
int A_num;
};
class B : virtual public A {
public:
B() { printf("B 생성자\n"); }
~B() { printf("B 소멸자\n"); }
private:
int B_num;
};
class C : virtual public A {
public:
C() { printf("C 생성자\n"); }
~C() { printf("C 소멸자\n"); }
private:
int C_num;
};
class D : public B, public C{
public:
D() { printf("D 생성자\n");
B::A_num = 0;
}
~D() { printf("D 소멸자\n");
}
private:
int D_num;
};
int main(void) {
D d;
return 0;
}
불필요한 소멸자와 생성자는 사라지는것이 보일겁니다.
하지만 저렇게 사용하면 메모리 상 공간이 증가된결과를 얻을수 있을겁니다.
이유는 가상 테이블이 생겨서 입니다.
보시면 vbptr(virtual base table pointer)이라는 것이 생겨서 입니다.
그리고 vbptr은 int A_num의 위치를 어디있는지 알려주는 역활을 합니다(offset 을 가르킴).
또한 int A_num의 위치가 메모리상 아래로 위치하게 되었습니다.
맨 아래로 간 이유는 중복을 막기위함이 제일 큽니다.
또한, 위치를 맨 아래를 보낸것은 offset정보를 계선할수 있기 때문입니다.
B의 vbptr의 오프셋을 보면 0, 20 입니다. 이걸 뭘 의미하냐면 자기 자신에서 20바이트 떨어진 곳에 A_num 변수가 있다라는 것을 알려주는 항목입니다.
시작 offset의 값은 D가 메모리의 적재되는 순간 해당 메모리의 기준으로 계산됩니다.
offset의 값은 -(음수)의 값이 될수도 있고 0 이 될수도 있습니다.
복잡하게 offset을 나눈 이유는 몇개의 상속이 올지도 모르기 때문입니다.
예측을 할수가 없기 떄문에 계산을 통해 위치를 알아내기 위해 만들어 놓은 것 입니다.
이렇게 되면 앞에 설명한 대로 최대의 단점인 메모리의 공간이 커지며, offset을 통해 자료를 찾는 과정에서 매우 큰 성능 저하를 유발할수 있습니다.
Reference
https://hwan-shell.tistory.com/224
'개발자 면접 공부 > C-C++' 카테고리의 다른 글
C++ STL 장단점 (0) | 2022.09.21 |
---|---|
C++의 인터페이스(Interface) (0) | 2022.09.17 |
C++ 객체지향 언어 4가지 특징 (0) | 2022.09.01 |
A* 알고리즘 (0) | 2022.08.24 |
다익스트라 vs BFS (0) | 2022.08.11 |