jam 블로그

[C++] 009. virtual의 원리와 다중 상속 본문

개발 및 관련 자료/C

[C++] 009. virtual의 원리와 다중 상속

kid1412 2013. 5. 12. 20:15
728x90

I. 클래스의 멤버 함수는 사실 어디에

  1.  #include <iostream>
  2. using namespace std;

  3. class Data
    {
     int data;
    public:
     Data(int num)
     {
      data = num;
     }
     void ShowData()
     {
      cout<<"data : "<<data<<endl;
     }
     void Add(int num)
     {
      data +=num;
     }
    };
  4. int main()
    {
     Data ddd(10);
     ddd.Add(10);
     ddd.ShowData();
     return 0;
    }
  •   구조체와 전역 함수로 위의 클래스를 흉내내 보자
  1. #include <iostream>
  2. using namespace std;
  3. struct Data
    {
     int data;
     void (*ShowData)(Data*);
     void (*Add)(Data*,int);
    };
  4. void ShowData(Data* THIS)
    {
     cout<<"Data : "<<THIS->data<<endl;
    }
    void Add(Data* THIS,int num)
    {
     THIS->data += num;
    }
  5. int main()
    {
     Data ddd1 = {10,ShowData,Add};
  6.  ddd1.Add(&ddd1,10);
  7.  ddd1.ShowData(&ddd1);
  8.  Data ddd2 = {1,ShowData,Add};
     ddd2.Add(&ddd2,1);
     ddd2.ShowData(&ddd2);
     return 0;
    }
  •  위의 소스를 실행하면 알수 있듯이 객체가 생성되면, 멤버 변수는 객체내에 존재하게된다. 그러나 멤버 함수는 메모리의 한 공간에 존재하면서, 모든 객체가 공유하는 형태를 취하게 된다.

 

II. 가상 함수가 동작하는 원리

  1.  #include <iostream>
  2. using namespace std;
  3.  class A
     {
      int a;
      int b;
  4.  public:
      virtual void fct1(){cout<<"fct1(...)"<<endl;}
      virtual void fct2(){cout<<"fct2(...)"<<endl;}
     };
  5.  class B : public A
     {
      int c;
      int d;
  6.  public:
      virtual void fct1(){cout<<"overriding fct1(...)"<<endl;}
      void fct3(){cout<<"fct3(...)"<<endl;}
     };
  7.  int main()
     {
      A* aaa = new A();
      aaa->fct1();
  8.   B* bbb = new B();
      bbb->fct1();
      return 0;
     }
  •   가상 함수가 하나 이상 있을 시 컴파일러는 가상함수 테이블이라는 것을 만들어 둔다.

    • 가상함수 테이블에서는 오버라이딩된 클래스의 가상 함수 fct1에 대한 정보는 포함되어있지 않다.
    • 즉, 하나 이상의 가상함수를 멤버로 지니는 클래스의 객체에는 가상함수테이블을 위한 포인터가 멤버로 추가된다.

 

 III. 다중 상속에 대한 이해

  • 다중 상속이란

    • 하나의 상속받는 클래스가 둘 이상의 기본 클래스를 상속하는 것을 말한다.
  1.  #include <iostream>
  2. using namespace std;
  3. class AAA
    {
    public:
     void String1()
     {
      cout<<"AAA:String1"<<endl;
     }
    };
  4. class BBB
    {
    public:
     void String2()
     {
      cout<<"BBB:String2"<<endl;
     }
    };
  5. class CCC:public AAA,public BBB
    {
    public:
     void ShowString()
     {
      String1();
      String2();
     }
    };

  6. int main()
    {
     CCC ccc;
     ccc.ShowString();
     return 0;
    }
  • CCC에서 AAA와 BBB를 둘다 상속 받았기 때문에 String1과 String2를 호출 할수 있다.

 

IV. 다중 상속의 모호성

  1.  #include <iostream>
  2. using namespace std;
  3. class AAA
    {
    public:
     void String()
     {
      cout<<"AAA:String1"<<endl;
     }
    };
  4. class BBB
    {
    public:
     void String()
     {
      cout<<"BBB:String2"<<endl;
     }
    };
  5. class CCC:public AAA,public BBB
    {
    public:
     void ShowString()
     {
      String();
      String();
     }
    };

  6. int main()
    {
     CCC ccc;
     ccc.ShowString();
     return 0;
    }
  • 위와 같은 경우 오류가 나는데 AAA에서나 BBB에서 String이라는 함수를 썼기 때문이다.

    • 따라서 ShowString에서 AAA::String, BBB::String 이런식으로 바꾸어 주어야 한다.

 

V. virtual Base 클래스

  1.  #include <iostream>
  2. using namespace std;

  3. class AAA
    {
    public:
     void String1()
     {
      cout<<"AAA::String"<<endl;
     }
    };
  4. class BBB:public AAA
    {
    public:
     void String2()
     {
      cout<<"BBB::String"<<endl;
     }
    };
  5. class CCC : public AAA
    {
    public:
     void String3()
     {
      cout<<"CCC::String"<<endl;
     }
    };
  6. class DDD:public BBB,public CCC
    {
    public:
     void ShowString()
     {
      String1();
      String2();
      String3();
     }
    };
  7. int main()
    {
     DDD ddd;
     ddd.ShowString();
     return 0;
    }
  • 위의 소스를 컴파일 하면 String1에서 오류가 난다.

    • 이유는 DDD에서 String1을 호출 하려고 하지만 상속받은 BBB를 참조해야할지 CCC를 참조해야할지 애매해서 오류가 난다.
  1. class BBB:virtual public AAA
    {
    public:
     void String2()
     {
      cout<<"BBB::String"<<endl;
     }
    };
  2. class CCC : virtual public AAA
    {
    public:
     void String3()
     {
      cout<<"CCC::String"<<endl;
     }
    };

- 이런식으로 AAA를 virtual로 상속 받으면 된다. 따라서 AAA클래스 안에 존재하는 멤버는 한번만 상속받게 된다.

Comments