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
반응형
'프로그래밍 > Effective C++' 카테고리의 다른 글
#define 안녕(effective c++ 02) (0) | 2015.07.27 |
---|---|
C++은 연합체로 바라보자(effective c++ 01) (0) | 2015.07.27 |
객체 복사(effective c++ 12) (0) | 2015.07.26 |
(작성중)배열 vs 다형성(more effective c++ 03) (0) | 2015.07.25 |
C++ 캐스팅(more effective c++ 02) (0) | 2015.07.25 |
객체 초기화(effective c++ 04) (0) | 2015.07.24 |
pointer vs reference (more effective c++ 01) (0) | 2015.07.21 |