이 물음에 답하기 위해 C언어 스타일로 Data클래스를 정의해서 설명함.
RealObjUnder2.cpp
#include <iostream>
using namespace std;
typedef struct Data
{
int data;
void (*ShowData)(Data*);
void (*Add)(Data*, int);
} Data;
void ShowData(Data* THIS) { cout << "Data: " << THIS->data << endl; }
void Add(Data* THIS, int num) { THIS->data += num; }
int main(void)
{
Data obj1 = { 15, ShowData, Add };
Data obj2 = { 7, ShowData, Add };
obj1.Add(&obj1, 17);
obj2.Add(&obj2, 9);
obj1.ShowData(&obj1);
obj2.ShowData(&obj2);
return 0;
}
이 예제에서 볼 수 있는 것은 두 개의 구조체 변수가 함수를 공유하고 있다는 점이다. 실제로 C++의 객체와 멤버함수는 이러한 관계를 갖고있음(멤버함수는 메모리의 한 공간에 별도로 위치하고선, 이 함수가 정의된 클래스의 모든 객체가 이를 공유하는 형태)
가상함수의 원리를 설명하기 위한 예제
VirutalInteral.cpp
#include <iostream>
using namespace std;
class AAA
{
private:
int num1;
public:
virtual void Func1() { cout << "Func1" << endl; }
virtual void Func2() { cout << "Func2" << endl; }
};
class BBB : public AAA
{
private:
int num2;
public:
virtual void Func1() { cout << "BBB::Func1" << endl; }
void Func3() { cout << "Func3" << endl; }
};
int main(void)
{
AAA* aptr = new AAA();
aptr->Func1();
BBB* bptr = new BBB();
bptr->Func1();
return 0;
}
한 개 이상의 가상 함수를 포함하는 클래스에 대해서는 컴파일러가 가상함수 테이블이라는 것을 만듦. AAA와 BBB에 대한 가상 테이블을 만들게 되는데 여기에선 Func1에 대해 오버라이딩을 하고 있기 때문에 BBB의 가상테이블엔 AAA::Func1 에 대한 정보가 존재하지 않음. --> 그래서 오버라이딩 된 가상 함수를 호출 시 항상 마지막에 오버라이딩을 한 유도 클래스의 멤버함수가 호출되는것!
MultiInheri1.cpp
#include <iostream>
using namespace std;
class BaseOne
{
public:
void SimpleFuncOne() { cout << "BaseOne" << endl; }
};
class BaseTwo
{
public:
void SimpleFuncTwo() { cout << "BaseTwo" << endl; }
};
class MultiDerived : public BaseOne, protected BaseTwo
{
public:
void CompleFunc()
{
SimpleFuncOne();
SimpleFuncTwo();
}
};
int main(void)
{
MultiDerived mdr;
mdr.CompleFunc();
return 0;
}
다중상속의 대상이 되는 두 기초 클래스에 동일한 이름의 멤버가 존재할 때 어느 클래스에 선언된 멤버에 접근을 하려는 것인 가에 관한 문제
관련 예제
MultiInheri2.cpp
#include <iostream>
using namespace std;
class BaseOne
{
public:
void SimpleFunc() { cout << "BaseOne" << endl; }
};
class BaseTwo
{
public:
void SimpleFunc() { cout << "BaseTwo" << endl; }
};
class MultiDerived : public BaseOne, protected BaseTwo
{
public:
void ComplexFunc()
{
BaseOne::SimpleFunc();
BaseTwo::SimpleFunc();
}
};
int main(void)
{
MultiDerived mdr;
mdr.ComplexFunc();
return 0;
}
가상함수를 사용하지 않고 문제를 해결하려면 어느 클래스에 정의된 함수의 호출을 원하는지 명시해야한다.
관련예제: MultiInheri3.cpp
#include <iostream>
using namespace std;
class Base
{
public:
Base() { cout << "Base Constructor" << endl; }
void SimpleFunc() { cout << "BaseOne" << endl; }
};
class MiddleDerivedOne : virtual public Base
{
public:
MiddleDerivedOne() : Base()
{
cout << "MiddleDerivedOne Constructor" << endl;
}
void MiddleFuncOne()
{
SimpleFunc();
cout << "MiddleDerivedOne" << endl;
}
};
class MiddleDerivedTwo : virtual public Base
{
public:
MiddleDerivedTwo() : Base()
{
cout << "MiddleDerivedTwo Constructor" << endl;
}
void MiddleFuncTwo()
{
SimpleFunc();
cout << "MiddleDerivedTwo" << endl;
}
};
class LastDerived : public MiddleDerivedOne, public MiddleDerivedTwo
{
public:
LastDerived() : MiddleDerivedOne(), MiddleDerivedTwo()
{
cout << "LastDerived Constructor" << endl;
}
void ComplexFunc()
{
MiddleFuncOne();
MiddleFuncTwo();
SimpleFunc();
}
};
int main(void)
{
cout << "객체생성 전 ..... " << endl;
LastDerived ldr;
cout << "객체생성 후 ....." << endl;
ldr.ComplexFunc();
return 0;
}
이 예제에서 중요한점은 Base의 유도 클래스 MiddleDerivedOne과 MiddleDerivedTwo 선언 시 base class 를 가상 상속을 하였다는 점이다. 이로 인해 main함수에서 LastDerived 객체를 선언하고 나서 ComplexFunc()함수 실행 시 바로 SimpleFunc()함수를 사용할 수 있게 되었다. 만약 가상함수를 사용하지 않았다면 Base클래스의 생성자는 두번 호출되고, SimpleFunc()함수 실행시 어떤 클래스의 함수를 사용할 것인지 앞에 명시를 해주어야 한다.