본문 바로가기
Basic/C/C++

[C++ 언어] 제 10 강 : virtual 그리고 다중 상속

by boxbop 2012. 9. 7.
반응형

 

 아주 오~~~랜만에 포스팅합니다! 너무 오래되서 전에 무슨 강을 공부했는지도 기억이 가물해서...;; 역시 사람의 망각 곡선은 무시할게 못되죠...ㅠㅠㅠㅠ 이럴때는 천재들이 부럽네요! 무튼 오늘은 virtual 의 원리와 다중상속에 대해서 공부해보도록 하겠습니다.

 

[가상 함수의 기본 원리]

 

 가상 함수가 동작하는 원리에 대해서 간단하게 알아보겠습니다.

 

#include <iostream>

using std::endl;

using std::cout;

 

class A

{

    int a;

    int b;

public:

    virtual void fct1() { cout<<"fct1(...)"<<endl; }

    virtual void fct2() { cout<<"fct2(...)"<<endl; }

};

 

class B : public A

{

    int c;

    int b;

public:

    virtual void fct1() { cout<<" overriding fct1(...)"<<endl; }

    void fct3() { cout<<"fct3(...)"<<endl; }

};

 

int main(void)

{

    A* aaa = new A();

    aaa->fct1();

 

    B* bbb = new B();

    bbb->fct1();

    return 0;

}

 

 먼저 A 클래스를 보면 virtual로 선언된 2개의 가상함수가 존재합니다. 마찬가지로 B 클래스에도 하나의 가상함수가 존재하지요. 중요한 부분은 B 클래스가 A 클래스를 상속했습니다만 fct1() 이라는 함수명이 중복되어 오버라이딩이 됩니다. 당연히 B 클래스 입장에서 fct1() 함수를 호출하면 "overriding fct1(...)"이 출력이 되겠죠? 마찬가지로 B 클래스 입장에서 fct2() 함수를 오출하면 A 클래스를 상속했으니 A 클래스의 fct2() 함수를 호출하게 되겠구요~

 

 즉, B 클래스의 입장에서 보게되면 오버라이딩된 A 클래스의 가상 함수 fct1에 대한 정보가 없기 때문에 B 클래스의 fct1 함수가 대신 호출 되는 것입니다. 이것이 바로 가상 함수의 기본원리 입니다.

 

[다중 상속에 대한 이해]

 

 결론 부터 말씀드리자면 다중 상속은 왠만하면 피해하는게 좋습니다. 이유를 간단하게 얘기하자면 클래스들의 관계가 복잡해지고, 관리하기에도 어렵습니다.

 

#include <iostream>

using std::cout;

using std::endl;

 

class AAA{

public:

   void String1() { cout<< " AAA::String1"<<endl; }

};

 

class BBB{

public:

   void String2() { cout<<" BBB::String2"<<endl; }

};

 

class CCC : public AAA, public BBB{

public:

   void ShowString()

   {

      String1();

      String2();

    }

};

 

int main(void)

{

   CCC ccc;

   ccc.ShowString();

   return 0;

}

 

 CCC 클래스는 AAA 클래스와 BBB 클래스를 동시에 상속하고 있습니다. 결과 값은 예상할 수 있겠죠? 딱히 문제되는 부분 없습니다. 자 여기서 다중 상속을 할 경우 주의해야 할 사항이 몇 가지 있습니다. 다음 예제를 통해서 살펴보도록 하겠습니다.

 

#include <iostream>

using std::cout;

using std::endl;

 

class AAA{

public:

void String() { cout<< " AAA::String"<<endl; }

};

 

class BBB{

public:

void String() { cout<<" BBB::String"<<endl; }

};

 

class CCC : public AAA, public BBB{

public:

  void ShowString()

  {

    String();

    String();

   }

};

 

int main(void)

{

    CCC ccc;

    ccc.ShowString();

    return 0;

}

 

 AAA 클래스와  BBB 클래스가 지니고 있는 맴버 함수의 이름이 똑같습니다. 때문에 컴파일 에러가 발생하게 됩니다. 무엇을 호출해야될지 모르기 때문이죠. 때문에 다음과 같이 변경해주어야 합니다.

 

 String() ----> AAA::String();

 String() ----> BBB::String();

 

위와 같은 다중상속의 모호성은 이렇게 쉽게 해결해보았습니다.

 

#include <iostream>

using std::cout;

using std::endl;

 

class AAA{

public:

void String1() { cout<< " AAA::String"<<endl; }

};

 

class BBB : public AAA{

public:

void String2() { cout<<" BBB::String"<<endl; }

};

 

class CCC : public AAA{

public:

void String3() { cout<<" CCC::String"<<endl; }

};

 

class DDD : public BBB, public CCC{

public:

   void ShowString()

   {

      String1();

      String2();

      String3();

    }

};

 

int main(void)

{

   DDD ddd;

   ddd.ShowString();

 

   return 0;

}

 

 DDD 클래스가 결과적으로 AAA 클래스를 두번이나 상속하게 되어있습니다. 이게 바로 문제가 되는 부분입니다. 즉 AAA 클래스의 맴버 함수 String1 을 BBB 클래스를 통해서, 그리고 CCC 클래스를 통해서 상속받고 있게되죠. DDD 클래스의 볼드 처리된 부분을 보시면 이 String1이 BBB 클래스를 통해서 상속받은 함수인지 CCC 클래스를 통해서 상속받은 함수인지 어떠한 함수를 호출할지 몰라서 결국 컴파일 에러를 발생시킵니다.

 

 이러한 문제는 virtual 상속을 통해서 간단하게 해결 할 수 있습니다.

 

class BBB : virtual public AAA{

public:

void String2() { cout<<" BBB::String"<<endl; }

};

 

class CCC : virtual public AAA{

public:

void String3() { cout<<" CCC::String"<<endl; }

};

 

 둘다 모두 AAA 클래스를 virtual 상속하고 있습니다. 반드시 둘다 virtual 상속을 해야합니다. 이제부터는 BBB 클래스와 CCC 클래스를 다중 상속한다 하더라도, AAA 클래스 안에 존재하는 맴버들은 한번만 상속이 이뤄지게 됩니다. 이것이 바로 virtaul 상속을 하는 이유입니다.

 

 결론은 다중 상속은 클래스의 관계를 복잡하게 흐려놓습니다. 다중 상속으로 밖에 해결이 안 되는 문제도 존재하지 않는다고 하니까 가급적이면 사용하지 않는 편이 좋다고하죠^^;; 오랜만에 포스팅하니까 현기증이..... 이번강은 여기서 마치도록 하겠습니다.

 

반응형