[C++] virtual의 원리와 다중상속
REF : 윤성우의 열혈 C++
가상함수와 멤버함수
멤버함수
- 객체를 생성할때마다 함수를 생성하는 것은 굉장한 메모리낭비가 아닐 수 없다.
- 멤버함수는 객체에 종속되는 개념이 아닌, 클래스에 종속되는 개념으로 존재한다.
- 클래스마다 존재한다.
- 클래스의 자료형으로 생성된 모든 객체가 멤버함수를 공유한다.
가상함수의 동작원리와 가상함수 테이블
- 한개 이상의 가상함수를 포함하는 추상클래스는 컴파일러가
가상함수 테이블
을 만든다.- 이는 실제 호출되어야 할 함수의 위치정보를 담는 테이블이다.
- 이를 V-Table이라고도 한다.
- 오버라이딩된 가상함수의 주소정보는 유도 클래스의 가상함수 테이블에 포함되지 않으므로,
- 가장 마지막에 오버라이딩 한 유도클래스의 멤버함수가 호출된다.
#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;
}
가상함수 테이블이 참조되는 방식
- 위의 그림처럼 가상함수를 가진 클래스는 가상함수 테이블이 만들어진다.
- 그리고 이 클래스로 객체를 생성하게되면 이 객체에는 가상함수 테이블의 주소값이 저장되는데, 실제로 우리가 사용할 수는 없다.
다만, 가상함수 테이블이 만들어지고, 참조하는 과정이 추가되므로 실행속도가 저하되지만, 속도차이는 미미하다.
다중상속에 대한 이해
견해
다중상속은 득보다 실이 많은 문법인가?
- 일반적인 경우에서 많은 문제가 발생한다.
- 따라서 가급적 사용하지 말아야하나, 제한적인 상황에서는 사용해야한다.
공부하지 않아도 되는가?
- 다중상속은 우리가 직접 구현할 필요가 없을수도 있으나,
- 다른 라이브러리가 사용했을수도 있으니, 기본적인 이해는 필요하다.
- 사용법
- 상속의 대상이되는 클래스를 구분해서 명시할 수 있다.
-
기초 클래스를 상속하는 형태는 각각 별도로 지정할 수 있다.
class A {}; class B {}; class AB : public A, protected B {};
다중상속의 문제점
- 모호성
- 다른 기초클래스에 동일한 멤버가 존재하는 경우에 유도클래스 내에서 멤버이름만으로 접근할 수 없다.
- 이름공간을 명시하면 가능하다.
class A { public: void simplefnc(); }; class B { public: void simplefnc(); }; class AB : public A, protected B { public: void exec() { A::simplefnc(); //namespace 붙여서 B::simplefnc(); //어느 클래스의 함수를 호출할것인지 명시 } };
- 가상상속
- 만약 가장 상위클래스를 기초클래스로 하는 유도 클래스 2개를 다중상속한다고 할때,
class Z { public: void basefnc(); }; class A : public Z { public: void simplefnc(); }; class B : public Z { public: void simplefnc(); }; class AB : public A, public B { public: void exec() { A::basefnc(); //namespace 붙여서 B::basefnc(); //어느 클래스의 함수를 호출할것인지 명시 } };
- 이런식으로 상속하게되면
AB 객체는 Z라는 기초 클래스 멤버를 2개나 가지게된다.
- 이에따라, Z클래스의 생성자가 2번호출된다.
- 최상위 기초클래스의 멤버를 가져올때도 어떤 유도클래스(A,B)의 기초 클래스 멤버 Z로부터 가져올 지 명시해줘야한다.
- 대부분의 경우, 클래스 AB가 간접상속한 Z는 1개만 존재하는게 타당한데,
- 이를 가능케하는게
가상 상속
이다.class Z { public: void basefnc(); }; class A : virtual public Z { public: void simplefnc(); }; class B : virtual public Z { public: void simplefnc(); }; class AB : public A, public B { public: void exec() { basefnc(); //namespace 붙여서 } };
- 가상상속을 통해, Z클래스의 멤버는 1개만 존재하게된다.
- 이를 가능케하는게
- 이런식으로 상속하게되면
- 만약 가장 상위클래스를 기초클래스로 하는 유도 클래스 2개를 다중상속한다고 할때,
댓글남기기