티스토리 뷰

Programming/C++

[c++]가상상속(virtual)

쩨리쩨리 2018. 3. 13. 12:04
반응형

* 다형성

객체들의 타입이 다르면 똑같은 메시지가 전달되더라도 서로 다른 동작을 하는 것을 말한다. 즉, 똑같은 명령을 내리지만 객체의 타입이 다르면 서로 다른 결과를 얻는 것이다. 중요한 것은 메시지를 보내는 측에서는 객체가 어떤 타입인지 알 필요가 없다는 점이다. 실행시간에 객체의 타입에 따라서 자동적으로 적합한 동작이 결정된다. 다형성은 객체 지향 기법에서 하나의 코드로 다양한 타입의 객체를 처리하는 중요한 기술이다.

 

 

 

* 가상상속(virtual)

java에선 단일상속이 되지만, c++에선 다중상속이 가능하다. 다중상속을 하면 여러군데로 상속을 하러 가기 때문에 모호성이 발생할 수 있는데, 명확성을 중요시하는 c++에서 모호성이 발생하면 에러가 뜨게 된다. 다중상속으로 모호성이 발생했을때 가상상속으로 상속받으면 상속받은 모호한 부모의 기능이 명확하게 바뀌게 된다. 

 

 

 

* 가상상속 할때 지켜야 할 점

1. 상속 방식(접근지정자)을 명확하게 써라(디폴트는 무조건 private 상속)

2. 모호성이 발생하지 않도록 하라(명확성 필수, 모호성이 나타날수 밖에 없으면 가상상속을 써라)
3. 외부에서 부모에게 값을 넘길때는 콜론 초기화를 해야한다.

 

* 가상상속 구조 이해(그림 ver)

 

 

 

A,B,C,D class 가 있다. A는 super class다. A class는 B,C class에게 다중 상속을 하고 있다. D class는 B,C  class에게 상속 받고 있다. D class에서 A class의 메소드를 호출하려한다. 이때, 에러가 뜬다. 왜 일까?

 

맨 처음 그림을 다시 그려보면 위의 그림처럼 된다. D class는 A class를 한번 호출했지만 결국 2개의 A class 메소드를 불러오고 있는것이다. D class는 2개의 A class 중에서 어떤 것을 불러올지 모르기 때문에 에러가 뜬 것이다. 만약 이런 상속 구조일때 D는 A class를 불러 올 수 없는 것일까? 방법은 있다. 바로 가상(virtual)상속을 쓰면 된다. A class를 상속받는 B class와 C class에게 A class를 가상으로 상속 받는다. 가상상속은 2개의 A class를 1개의 A class로 보이도록 해준다.

 

 

가상상속을 썼을때 그림으로 보면 위의 형태이다. 가상상속은 2개의 A class를 1개의 A class로 보이도록 하는 역할을 하기에 D class가 1개의 A class를 부를수 있는 형태로 바뀐다. A class가 명확하게 1개만 존재하기 때문에 D class는 에러 없이 A class의 기능들을 불러 올 수 있다.

위 그림을 더 상세하게 바꾸면 아래의 그림이라고 할 수 있다.

 

A class가 가상상속을 했기 때문에 A는 가상의 class가 되어서 D class에서는 A class가 몇개인지 알 수 없게된다. 이럴때 D class는 A class를 1개로 인식을 해버린다. 실제론 다중 상속을 하고 있는 구조인데 말이다.

 

 

 

 

* 가상상속 구조 이해(코딩 ver)

D의 객체 dd는 1,2,3,4의 숫자를 포함하고 있다. A,B,C,D class에서는 모두 disp() 함수가 있다. dd의 객체가 객체를 생성하러 객체를 초기화 하러 인자를 보낸다. 이때, 가상상속을 이용하여 A의 disp()에서는 1을, B의 disp() 에는 2를, C의 disp() 에는 3을, D의 disp() 에는 4를 출력하도록 하시오.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<iostream>
#include<string>
using namespace std;
 
 
class A {
 
    int a;
public:
    //A() {}
    A(int a){this->= a;}
 
    void dispA() {cout << a << endl;}
 
};
class B :virtual public A{//A 가상 상속 
    int b;
public:
    //B() {}
    B(int a,int b) :A(a){this->= b;}
    void dispB() {cout << b << endl;}
 
};
class C : virtual public A{//A가상 상속
 
    int c;
public:
    //C() {}
    C(int a,int c) :A(a){this->= c;}
    void dispC() {cout << c << endl;}
 
};
class D :public B,public C{//B,C 다중상속
 
    int d;
public:
    //D() {}
    D(int a,int b,int c,int d):B(a,b),C(a,c),A(a) {
    //main에서 받은 인자를 다른 class로 보낸다.
    //= :d(d),A(a),B(b),C(c)
        this->= d;}
    void dispD() {cout<<<< endl;}
 
};
 
void main() {
 
    D dd(1,2,3,4);//값을 준다. dd 객체를 만들러 D의 생성자로 간다.
 
    dd.dispD();//4
    dd.dispC();//3
    dd.dispB();//2
    dd.dispA();//1
}
cs

 

 * 풀이

D class의 생성자는 인자를 4개 받는다. 그리고 각각 class의 생성자에 인자를 보낸다. B class는 A를 상속받고 있기 때문에, A의 생성자도 디폴트로 포함하고 있다. 그래서 B에서 b를 출력 시키려면 a를 같이 보내야  b를 받을 수 있다. 따라서 C class도 a와 c를 같이 보내야한다. A class 에서 a를 받기 위해선 A class에도 a를 보내야한다. 위의 D class 생성자를 더 간단히 요약하면 아래 코드처럼도 표현할 수 있다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
class D :public B,public C{
 
    int d;
public:
    D() {}
    D(int a,int b,int c,int d):A(a),B(b),C(c) {
        this->d = d;
    }
    void dispD() {
        cout<<d << endl;
    }
 
};
cs

 

 

 

 

 

 

 

 

 

반응형
댓글
공지사항