ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • virtual 응용(원리, 다중상속)
    플밍/C++ (overview) 2012. 1. 3. 23:14
    2006/08/19 13:34



    클래스의 멤버 함수는 실제로 어디에?

     

    여지껏 멤버 변수와 멤버 함수는 모두 객체 내에 존재한다고 얘기해왔다.

    (그렇게 생각하고 프로그래밍 해도 상관은 없다.)

     

    그러나, 우리는 실체를 알고는 있어야 한다.

     

    멤버 변수는 각 객체 내에 존재하는게 맞지만, 멤버 함수는 아니다.

     

    멤버 함수는 어짜피 같은 일을 하기때문에 각 객체 내에 두기엔 공간 낭비가 심하다.

    그래서 한곳에 멤버 함수를 두고, 각 객체가 그 함수를 복사해 쓰는 식으로 한다.

     

    ========================================================================================

     

    가상 함수가 동작하는 원리

     

    가상 함수는 실제로는 그곳에 없는 함수라고 했는데, 그럼 대체 어떤 원리로 동작하는걸까?

     

    가상함수를 하나라도 가지고 있는 클래스에 대해서, 컴파일러는 가상함수테이블(VTable)

    이라는것을 만들어준다.

     

    테이블에는 Key(함수이름정보)와 Value(함수가 위치하는 주소값)이 적혀있다.

     

    class A{
    public:
           virtual void fct1(){
                  cout << "fct1()" << endl;
           }
           virtual void fct2(){
                  cout << "fct2()" << endl;
           }
    };


    class B : public A{
    public:
           virtual void fct1(){
                  cout << "overriding fct1()" << endl;
           }
           void fct3(){
                  cout << "fct3()" << endl;
           }
    };

     

    int main(void){
           A * a = new A();
           a->fct1();

     

           B * b = new B();
           b->fct1();

    }

    위를 실행하면 a와 b를 위한 가상함수테이블이 만들어지는데,

    a의 가상함수테이블은 아래와 같다.

           Key         |     Value

    ----------------------------

    void A::fct1()   |  0x1100번지

    void A::fct2()   |  0x1200번지

     

    b의 가상함수테이블은 아래와 같다.

           Key         |     Value

    ----------------------------

    void B::fct1()   |  0x2100번지

    void A::fct2()   |  0x1200번지

    void B::fct3()   |  0x2200번지

     

    잘 살펴보면, b의 가상함수테이블에서 overriding된 A클래스의 fct1()에 대한정보는 없다!!

    (이것이 overriding을 가능하게 한다.)

     

    그래서 b의 fct1()을 호출했을때 자신이 가지고 있는 VTable을 참조해서 자기가 가지고 있는

    fct1()을 바로 실행할수 있는것이다. (자신이 overriding한 A의 fct1()에 대한 정보는 전혀없다.

    자신이 overriding한 함수인지도 모른다 -_-;)

     

    즉, 하나 이상의 가상함수를 멤버로 지니는 클래스의 객체에는 VTable을 가리키는 포인터가

    멤버로 추가된다! (우리가 직접 접근할수 있는 포인터는 아니다.)

     

    그말은 가상함수가 없는 클래스는 VTable을 위한 포인터가 만들어지지 않는다!

    호출할 함수에 직접 접근한다. 그래서 가상함수가 포함되면 전체적인 성능은 떨어지게 된다.

    그렇지만 많은 장점을 지니고 있기때문에 유용하게 사용될수 있다!

     

    ========================================================================================

     

    다중 상속

     

    class CCC : public A1, public A2, .... {

    };

     

    말그대로 하나의 클래스가 둘이상의 클래스를 상속하는 것을 뜻한다.

     

    하나만 알아두자! 이러한 문법이 존재하긴 하지만, 득보다는 실이 더 많은 문법이다.

    즉, 되도록이면 쓰지 말자!

     

    왜냐? 다음과 같은 애매한 상황도 발생할수 있기 때문이다.

     

    class AAA{

           void String();

    };

     

    class BBB{

           void String();

    };

     

    class CCC:public AAA, public BBB{

           void ShowString(){

                String();

                String();

           }

    };

     

    main(){

           CCC c;

           c.ShowString();

    }

     

    컴파일에러가 나는 부분을 보았는가?

    작성자의 의도는 AAA에 있는 String()한번, BBB에 있는 String()한번씩 쓰고 싶었나보다.

     

    그러나, 컴파일러는 어떤 String()이 어디에 속한 String()인지 알수 없다!

     

    해결책은 있다.

    명확히 범위지정연산자를 쓰는것이다.

     

    굵은 빨간글씨부분을 AAA::String(), BBB::String() 이라고 바꿔주면 되겠다.

     

    이런상황도 발생할수 있다!

    다음은, 다이아몬드 상속을 한 경우이다.

     

    class AAA{

           void String1();

    };

    class BBB : public AAA{

           void String2();

    };

    class CCC : public AAA{

           void String3();

    };

    class DDD : public BBB, public CCC{

           void ShowString(){

               String1();        // BBB꺼? CCC꺼? 누구꺼?

               String2();

               String3();

           }

    };

     

    위의 상속관계를 그림으로 그려보면, 다이아몬드가 나온다.(그려봐라 -_-)

     

    BBB는 String1, String2를 가지고 있고, CCC는 String1, String3을 가지고 있다.

    그 둘을 DDD가 상속했으니, String1, String2, String3을 가지게 되는데,

    String1은 둘다 가지고 있으니까 한쪽에서만 가져오면 될텐데.. 우리 컴파일러는 그런 생각을

    할수 없다. 이름 안 따지고 각각이 가지고 있는 함수를 모두 상속해와야 하니까!

    따라서 컴파일 오류가 난다!

     

    그러면, DDD가 같은 base클래스를 상속한 derived클래스들을 상속할때,

    derived클래스들이 가지고 있는, 동일한, base클래스의 함수 중 하나만 상속받게 하려면

    어떻게 할까?

     

    답은, virtual 상속이다!

    (다이아몬드 상속에서만 가능하다.)

     

    class AAA{

           void String1();

    };

    class BBB : virtual public AAA{

           void String2();

    };

    class CCC : virtual public AAA{

           void String3();

    };

    class DDD : public BBB, public CCC{

           void ShowString(){

                  String1();

                  String2();

                  String3();

           }

    };

     

    이제는 BBB클래스와 CCC클래스를 다중상속한다고 하더라도, AAA클래스 안에 존재하는

    멤버들은 한번만 상속이 이루어진다. 이것이 바로 virtual 상속을 하는 이유이다.

     

    복잡하다........... -_- 결론은, 왠만하면 이런형태의 상속은 쓰지 말자!



    '플밍 > C++ (overview)' 카테고리의 다른 글

    string 클래스 디자인  (0) 2012.01.03
    연산자 오버로딩  (0) 2012.01.03
    상속(심화), 두종류binding, virtual  (0) 2012.01.03
    상속  (0) 2012.01.03
    modifier 종류 : const, static, explicit, mutable  (0) 2012.01.03
Designed by Tistory.