private 상속에 대하여(effective c++ 39)

2015. 7. 19. 03:23프로그래밍/Effective C++

728x90
728x90
public 상속?
C++는 public 상속을 is-a 관계로 나타낸다.

private 상속을 하게 되면 어떤일이 생기나요?
- 컴파일러는 파생 클래스 객체를 기본 클래스 객체로 변환하지 않는다. 
- 베이스 클래스로부터 물려받은 멤버에서 파생 클래스에서 모조리 private 맴버가 된다. 
베이스 클래스에서 protected 이거나 public이어도.

private 상속은 그 자체로 구현 기법 중 하나.
- 구현만 물려받을 수 있으며 인터페이스는 허용하지 않는다.
- 소프트웨어 설계 도중에는 아무런 의미가 없다. 단지 소프트웨어 구현(implementation) 중에만 의미를 가진다.

현실적으로 private 상속 대신에 public 상속에 객체 합성 조합이 더 즐겨 쓰는 방법이긴하다.
장점은 무엇인가?
- 객체 합성 조합 클래스에서 가상 함수를 재정의할 수 없게 막을 수 있다. Base 클래스가 객체 합성 조합 클래스에서
private 맴버로 선언되어 있기때문에 객체 합성 조합 클래스의 Derived는 절대 가상 함수를 재정의 할 수 없다.
C++11부터 final이나 sealed로 원래 막을 수 있는데.. 이제 이걸 이유라고 할 수 있으려나..
- 객체 합성 조합 클래스의 컴파일 의존성을 최소화 할 수 있다. 해당 객체를 포인터로 받고 전방선언을 하면
컴파일 의존성을 피할 수 있다는 뜻.

객체 합성보다 상속을 강제하는 예외
EBCO는 객체 합성 조합보다 상속이 메모리 면에서 훨씬 이득이다.

정리
private 상속은 is-implemented-in-term-of! is-a 관계인지 클래스 설계시 확인하라.
객체 합성과 달리 private 상속은 EBO를 활성화 시킬 수 있다
꼭 private 설계만이 좋은것은 아니다. 여러 갈래로 설계하라.

소스코드
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// PrivateInheritance.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
// 작성자 : 조민혁 작성일 : 2015.07.19
 
#include "stdafx.h"
 
class TV
{
public:
    TV() : m_channel(0) { };
    ~TV() { };
 
public:
    void upChannel() { m_channel++; }
    void downChannel() { m_channel--; }
 
    virtual void OnTV() { printf("power on using TV\n"); }
protected:
    int m_channel;
};
 
class RemoteController1 : public TV
{
public:
    RemoteController1() { };
    ~RemoteController1() { };
 
private:
     
};
 
class RemoteController2 : private TV
{
public:
    RemoteController2() { };
    ~RemoteController2() { };
 
public:
    
    void changeChnnel(int nChannel)
    {
        while (m_channel != nChannel)
        {
            if (m_channel < nChannel)
            {
                upChannel();
            }
            else
            {
                downChannel();
            }
        }
    }
    // 부모의 함수를 사용할 수 없는 private 상속이므로 
    // 이 함수를 사용하려면 반드시 다시 구현해줘야한다.
    virtual void OnTV() final { printf("power on using remocon\n"); }
    /* 미구현시 에러
        error C2247: 'TV::OnTV'에 액세스할 수 없습니다. 이는 'RemoteController2'이(가) 
        'private'을(를) 사용하여 'TV'에서 상속하기 때문입니다.
        cpp(16) : 'TV::OnTV' 선언을 참조하십시오.
        cpp(31) : 'RemoteController2' 선언을 참조하십시오.
        cpp(7) : 'TV' 선언을 참조하십시오.
    */
private:
    
};
 
class RemoteController3
{
public:
    RemoteController3() { };
    ~RemoteController3() { };
 
    void changeChnnel(int nChannel)
    {
        while (m_remote.getChannel() != nChannel)
        {
            if (m_remote.getChannel() < nChannel)
            {
                m_remote.upChannel();
            }
            else
            {
                m_remote.downChannel();
            }
        }
    }
 
    void OnTV() { m_remote.OnTV(); }
 
private:
    class RemoteTV : public TV 
    { 
    public:
        inline int getChannel() { return m_channel; }
        virtual void OnTV() { printf("power on using remocon\n"); } 
    };
    RemoteTV m_remote;
};
 
class RemoteDerived : public RemoteController3
{
public:
    RemoteDerived() { }
    ~RemoteDerived() { }
    // 파생 클래스에서 가상함수를 정의할 수 없다.
    // m_remote가 private 맴버이므로!
    // m_remote.
private:
 
};
 
class Empty { };
 
class intClass1
{
public:
    intClass1() { }
    ~intClass1() { }
 
private:
    int m_int;
    Empty e;
};
 
class intClass2 : private Empty
{
public:
    intClass2() { }
    ~intClass2() { }
 
private:
    int m_int;
};
 
 
int _tmain(int argc, _TCHAR* argv[])
{
    RemoteController1 publicRemote;
    RemoteController2 privateRemote;
    RemoteController3 innerClassRemote;
 
    TV* ptrTV = NULL;
 
    //---------- 01. 상속관계 테스트----------
    // 가능 public 상속이므로 is-a 관계이기 때문에 가능하다.
    ptrTV = &publicRemote;
 
    // 불가능 TV != 리모콘
    // ptrTV = &privateRemote;
    /*
        1> error C2243: '형식 캐스팅' : 'RemoteController2 *'에서 'TV *'(으)로의 변환이 있지만 액세스할 수 없습니다.
    */
 
    // 역시 불가능
    // ptrTV = &innerClassRemote;
    /*
        1> error C2440: '=' : 'RemoteController3 *'에서 'TV *'(으)로 변환할 수 없습니다.
    */
 
    //---------- 02. 함수호출 테스트----------
    // pulic 상속에선 문제 없이 사용이 가능하다.
    publicRemote.upChannel();
 
    // public 상속이므로 오버라이딩 하지 않아도 부모의 함수를 이용한다.
    publicRemote.OnTV();
 
    // private 상속에선 upChannel은 접근이 불가능하다. 
    // privateRemote.upChannel();
    /*
        1> error C2247: 'TV::upChannel'에 액세스할 수 없습니다. 이는 'RemoteController2'이(가) 
            'private'을(를) 사용하여 'TV'에서 상속하기 때문입니다.
        1> 'TV::upChannel' 선언을 참조하십시오.
        1> 'RemoteController2' 선언을 참조하십시오.
        1> 'TV' 선언을 참조하십시오.
    */
 
    // 그래서 해당 방법으로 메소드 구현
    privateRemote.changeChnnel(5);
    privateRemote.OnTV();
 
    // innerClass 방식은 함수 자체가 나오지 않는다. 
    // innerClassRemote.
    innerClassRemote.changeChnnel(10);
    innerClassRemote.OnTV();
 
    //---------- 03. 빈 객체 테스트
    printf("intClass1's size = %d\n"sizeof(intClass1));
    printf("intClass2's size = %d\n"sizeof(intClass2));
    /*
        EBCO (Empty Base Class Optimization)
        객체 합성 방식보다 메모리 공간을 훨씬 절약할 수 있다.
    */
 
    return 0;
}
cs




728x90
반응형