문과 코린이의, [C. C++] 기록/C++ 이론

[문과 코린이의 IT 기록장] C,C++ - 가상(Virtual)의 원리와 다중상속 2 : 다중상속에 대한 이해 ( 다중상속에 대한 견해, 다중상속의 기본방법, 다중상속의 모호성, 가상 상속 )

벼리네 2021. 3. 10. 16:25
반응형

[문과 코린이의 IT 기록장] C,C++ - 가상(Virtual)의 원리와 다중상속 2 : 다중상속에 대한 이해 

( 다중상속에 대한 견해, 다중상속의 기본방법, 다중상속의 모호성, 가상 상속 )


 


 1. 다중상속에 대한 견해
- 다중상속이란 둘 이상의 클래스를 동시에 상속한다는 것을 말한다.
- 그러나, 다중상속은 꽤 논란이 발생하는 문법이다. 따라서, 다중상속을 매우 예외적으로, 제한적으로 사용할 필요가 있다.




 2. 다중상속의 기본방법
ex )

#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 ComplexFunc(){

// 다중상속을 통해, BaseOne 클래스의 맴버함수와, BaseTwo 클래스의 맴버함수각각 호출하고 있다.
   SimpleFuncOne();
   SimpleFunctwo();
 }
};

int main(){
MultiDerived mdr;
mdr.ComplexFunc();
return 0;
}




 3. 다중상속의 모호성
- 다중상속의 대상이 되는 두 기초 클래스에, 동일한 이름의 맴버가 존재하는 경우 문제가 발생 수 있다. 이 경우에는, 유도 클래스 내에서 맴버의 이름만으로 접근이 불가능하기 때문이다.


ex )

#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();
// BaseOne 클래스, BaseTwo 클래스, 모두 SimpleFunc() 맴버함수가 존재하기 때문에, 이 둘을 상속하는 유도 클래스에서 SimpleFunc() 맴버함수를 호출할 경우에는, 어느 클래스에 정의된 함수를 호출해야 하는지 명시해야 한다.
 }
};

int main(){
MultiDerived mdr;
mdr.ComplexFunc();
return 0;
}





 4. 가상 상속
- 함수 호출관계의 모호함은 다른 상황에서도 발생할 수 있다.

 


ex )

#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(){

// 가상상속(virtual 선언) 때문에, Base 클래스의 생성자가 1번만 호출되는 것을 볼 수 있다.
// 기초 클래스의 생성자 호출을 명시하지 않아도 되지만, 호출 여부에 대해 알아보기 위해 명시함.
   cout<<"LastDerived Constructor"<<endl;
 }
 void ComplexFunc(){
   MiddleFuncOne();
   MiddleFuncTwo();
   SimpleFunc();
 }
};

int main(){
cout<<"객체생성 전 ...."<<endl;
LastDerived ldr;
cout<<"객체생성 후 ...."<<endl;
ldr.ComplexFunc();
return 0;
}



a. 가상상속 X
- 만약 virtual 선언을 하지 않은 상태에서, 객체가 생성될 경우, 아래와 같은 형태가 된다.
 * Base 클래스의 맴버가 두 번 포함됨

 

[ 상속의 구조 ]

 

 

상속의 구조

 

 


[ Base 클래스를 두 번 상속하는 LastDerived 클래스의 객체 ]

 

 

 

- 이와 같이 가상상속을 하지 않을 경우, 하나의 객체 내에서 두 개의 Base 클래스 맴버가 존재하기 때문에, ComplexFunc() 함수 내에서 이름만 가지고 SimpleFunc()함수를 호출할 수는 없다.

- 따라서, 어떤 클래스를 통해 간접 상속한 Base() 클래스의 맴버함수를 호출할 것인지 명시해야 한다.

ex ) 

MiddleDerivedOne :: SimpleFunc(); // MiddleDerivedOne 클래스가 상속한, Base 클래스의 SimpleFunc() 함수호출 명령

MiddleDerivedTwo :: SimpleFunc(); // MiddleDerivedTwo 클래스가 상속한, Base 클래스의 SimpleFunc() 함수호출 명령

 

 

- 사실, Base 클래스의 맴버는 LastDerived 객체에 하나씩만 존재하게 하는 것이 좋다. 따라서, 이를 해결하기 위해서 '가상 상속' 사용이 필요하다.

 


b. 가상 상속 O

 

ex )

class MiddleDerivedOne : virtual public Base { .... };

class MiddleDerivedTwo : virtual public Base { .... };

class LastDerived : public MiddleDerivedOne , public MiddleDerivedTwo { .... };

 

- 이와 같이 가상으로 상속된 두 클래스를, 다중 상속하게 되면, LastDerived 객체 내부에는, MiddleDerivedOne 클래스와 MiddleDerivedTwo 클래스가 동시에 상속하는 Base 클래스 맴버가 하나씩만 존재하게 된다.

- 따라서, 예제에서 문제 없이 SimpleFunc() 함수를 이름만 가지고 호출 수 있다.

 

 


* 유의사항
- 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다.
- 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다.
- 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :)
반응형