멤버 함수와 가상 함수의 동작 원리
- 객체가 생성되면 멤버 변수는 객체 내에 존재한다.
- 멤버 함수는 메모리의 한 공간에 별도로 위치하고 이 함수가 정의된 클래스의 모든 객체가 이를 공유하는 형태를 취한다.
- 한 개 이상의 가상 함수를 포함하는 클래스에 대해서는 컴파일러가 가상 함수 테이블을 만든다.
- 가상 함수 테이블은 객체의 생성과 상관없이
main
함수가 호출되기전 메모리 공간에 할당된다.
- 가상 함수 테이블은 호출되어야 할 함수의 위치정보를 담고 있는 테이블이다.
class A
{
public:
virtual void Func() { cout << "A" << endl; }
};
class B : public A
{
public:
void Func() { cout << "B" << endl; }
};
int main()
{
A* a = new A();
a->Func(); // 결과는 A
A* b = new B();
b->Func(); // 결과는 B
return 0;
}
A <- B
구조에서 B 클래스의 가상 함수 테이블을 살펴보면, 오버 라이딩된 A클래스의 Func
함수에 대한 정보가 없다.
- 가상 함수의 호출 원리로 b의
Func
함수를 호출하면 B클래스의 Func
함수가 호출된다.
다중 상속
class A
{
public:
void Func() { }
};
class B
{
public:
void Func() { }
};
class C : public A, public B
{
public:
void Call Func()
{
Func(); // 모호성 존재
A::Func();
B::Func();
}
};
- 어느 클래스에 선언된 멤버에 접근을 하는지에 대해 모호성이 존재한다.
- A, B 클래스가 같은 이름의 함수를 가지고 있다면
A::Func();
, B::Func();
로 클래스를 명시해야 한다.
가상 상속
class Base
{
public:
Base() { cout << "Base Constructor" << endl; }
void BaseFunc() { cout << "Base" << endl; }
};
class Middle1 : virtual public Base
{
public:
Middle1() : Base() { cout << "Middle1 Constructor" << endl; }
void Middle1Func()
{
BaseFunc();
cout << "Middle1" << endl;
}
};
class Middle2 : virtual public Base
{
public:
Middle2() : Base() { cout << "Middle2 Constructor" << endl; }
void Middle2Func()
{
BaseFunc();
cout << "Middle2" << endl;
}
};
class Last : public Middle1, public Middle2
{
public:
Last() : Middle1(), Middle2() { cout << "Last Constructor" << endl; }
void LastFunc()
{
Middle1Func();
Middle2Func();
BaseFunc();
}
};
int main()
{
Last last;
last.LastFunc();
return 0;
}
- 다중 상속을 하면
last
생성 시 base
생성자가 두 번 호출된다.
Middle1
, Middle2
에서 가상 상속을 통해 가상 상속을 하면 base
생성자가 한 번만 호출되도록 할 수 있다.