[열혈 C++] Chap09: 가상(Virtual)의 원리와 다중상속
1. 멤버함수와 가상함수 동작원리
✏️그것이 알고싶다: 멤버함수는 과연 객체 안에 존재하나?
-
객체가 생성되면 멤버변수는 객체 내에 존재하지만, 멤버함수는 메모리의 한 공간에 별도로 위치하고선, 이 함수가 정의된 클래스의 모든 객체가 이를 공유하는 형태를 취한다
-
함수는 그럼 기본적으로 static이 선언된 것과 다름 없다는 건가?
✏️가상함수의 동작원리와 가상함수 테이블
-
가상함수 : 포인터 변수가 실제로 가리키는 객체의 함수를 호출하게 함
-
한 개 이상의 가상함수가 포함된 클래스의 객체가 생성될 때, 컴파일러는 가상함수 테이블을 만든다
+----------+----------+ | key | value | +----------+----------+ | Func | address | +----------+----------+ | Func | address | +----------+----------+
-
V-Table이라고도 한다. 실제 호출되어야하는 함수 위치정보를 담고있다.
-
이때, 오버라이딩 된 기초클래스의 가상함수에 대한 정보는 유도클래스의 테이블에 포함되지 않는다
-
객체 생성과 상관없이 메모리 공간에 할당된다
-
✏️가상함수 테이블이 참조되는 방식
출처: 네이버 블로그/치킨런: https://blog.naver.com/kbm0996/221114385074
- 위와 같은 과정이 있기 때문에 실행속도가 감소하기 마련이다. 그러나 속도가 느려지는 미미한 단점보다 장점이 더 유용하여 사용하게 된다.
2. 다중상속에 대한 이해
✏️다중상속
-
득/실에 대한 견해가 갈림
-
모호성 : 기초클래스의 멤버 이름이 겹칠 경우 호출할 때 문제 발생
#include <iostream>
using namespace std;
class BaseOne
{
public:
void SimpleFunc() { cout<<"BaseOne"<<endl; }
};
class BaseTwo
{
public:
void SimpleFunc() { cout<<"BaseTwo"<<endl; }
};
class MultiDrived : public BaseOne, protected BaseTwo
{
public:
void ComplexFunc()
{
BaseOne::SimpleFunc(); //출처를 밝히면 된다
BaseTwo::SimpleFunc();
}
};
✏️가상상속
#include <iostream>
using namespace std;
class Base
{
public:
Base() { cout<<"Base Constructor"<<endl; }
void SimpleFunc() { cout<<"BaseOne"<<endl; }
};
class MiddleDrivedOne : virtual public Base
{
public:
MiddleDrivedOne() : Base()
{
cout<<"MiddleDrivedOne Constructor"<<endl;
}
void MiddleFuncOne()
{
SimpleFunc();
cout<<"MiddleDrivedOne"<<endl;
}
};
class MiddleDrivedTwo : virtual public Base
{
public:
MiddleDrivedTwo() : Base()
{
cout<<"MiddleDrivedTwo Constructor"<<endl;
}
void MiddleFuncTwo()
{
SimpleFunc();
cout<<"MiddleDrivedTwo"<<endl;
}
};
class LastDerived : public MiddleDrivedOne, public MiddleDrivedTwo
{
public:
LastDerived() : MiddleDrivedOne(), MiddleDrivedTwo()
{
cout<<"LastDerived Constructor"<<endl;
}
void ComplexFunc()
{
MiddleFuncOne();
MiddleFuncTwo();
SimpleFunc();
}
};
int main(void)
{
cout<<"객체생성 전 ..... "<<endl;
LastDerived ldr;
cout<<"객체생성 후 ..... "<<endl;
ldr.ComplexFunc();
return 0;
}
- 위 코드에서 LastDerived의 객체는 Base를 두개 갖고있는 셈이 된다.
+------------------+ +-------------------+
| +-----------+ | | +-----------+ |
| | Base | | | | Base | |
| +-----------+ | | +-----------+ |
| +-----------+ | | +-----------+ |
| | MiddleOne | | | | MiddleTwo | |
| +-----------+ | | +-----------+ |
+------------------+ +-------------------+
\ /
\ /
LastDerived
-
이런 상황에서 Base클래스 멤버가 하나씩만 존재하는게 타당하기 때문에 가상상속을 통해 해결한다.
-
class MiddleDrivedOne : virtual public Base
,class MiddleDrivedTwo : virtual public Base
이렇게 가상상속을 하는 클래스를 다중상속 받으면 멤버가 하나만 생기게 된다
+--------------+ +--------------+
| +---------------+ |
| | Base | |
| +---------------+ |
| MiddleOne | | MiddleTwo |
+--------------+ +--------------+
\ /
\ /
LastDerived