일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 반복자
- reference
- 영화 리뷰
- lua
- 스마트 포인터
- 비교 함수 객체
- 메타테이블
- 암시적 변환
- 언리얼
- Effective c++
- 티스토리챌린지
- implicit conversion
- c++
- more effective c++
- 오블완
- operator new
- 상속
- effective stl
- exception
- 다형성
- 루아
- 영화
- resource management class
- Smart Pointer
- Vector
- UE4
- 참조자
- virtual function
- 게임
- 예외
Archives
- Today
- Total
스토리텔링 개발자
[Effective C++] 40. 다중 상속 본문
728x90
항목 40 : 다중 상속은 심사숙고해서 사용하자
다중 상속(multiple inheritance : MI)의 문제
- 둘 이상의 기본 클래스로부터 똑같은 이름을 물려받을 가능성이 생긴다.
-
class BorrowableItem { public: void checkOut(); ... }; class ElectronicGadget { private: bool checkOut() const; ... }; // 이런 다중 상속의 경우 어떻게 될 것인가? class MP3Player : public BorrowableItem, public ElectronicGadget { ... }; MP3Player mp; mp.checkOut(); // 모호성 발생!
- 위에서 확인할 수 있듯, 파생 클래스가 접근할 수 있는 함수가 딱 결정되는 게 분명해도 모호성이 발생한다.
- BorrowableItem에서는 public 멤버이지만, ElectronicGadget에서는 private 멤버이다.
- 하지만 이 경우에도 모호성이 발생한다.
- C++ 컴파일러가 중복된 함수 호출 중 하나를 골라내는 규칙
- 최적 일치 함수를 찾는다.
- 함수의 접근 가능성을 점검한다.
- 위의 경우 일치도가 서로 같기 때문에 최적 일치 함수가 결정되지 않고, 그렇기에 함수 접근 가능성 점검조차 하지 않고 모호성이 발생하는 것이다.
- 해결법
- 명확히 사용하고자 하는 함수를 명시하자.
-
mp.BorrowableItem::checkOut();
-
- 죽음의 MI 마름모꼴(deadly MI diamond)
-
class File { ... }; class InputFile : public File { ... }; class OutputFile : public File { ... }; // 죽음의 MI 마름모꼴 발생! // File의 데이터 멤버가 중복 생성된다!! class IOFile : public InputFile, public OutputFile { ... };
- 기본 클래스와 파생 클래스 사이 경로가 두개 이상 되는 상속 계통.
- 기본 클래스 데이터 멤버가 중복 생성된다 vs 기본 클래스 데이터 멤버가 중복되지 않도록 한다.
- 무엇이 맞는 것일까? 둘 다 맞는 거 같기도 한데..
- C++은 두 가지 방법 모두를 지원한다.
- 기본적으로는 기본 클래스의 데이터 멤버를 중복 생성한다.
- 기본 클래스를 가상 기본 클래스(virtual base class)로 만드는 것으로 중복 생성하지 않도록 할 수 있다.(가상 상속)
-
가상 상속(virtual inheritance)
- 가상 기본 클래스로 삼을 클래스에 직접 연결된 파생 클래스에서 가상 상속(virtual inheritance)을 사용하게 만든다.
-
class File { ... }; // 가상 상속 class InputFile : virtual public File { ... }; class OutputFile : virtual public File { ... }; class IOFile : public InputFile, public OutputFile { ... }
- 표준 C++ 라이브러리에서 가상 상속을 활용하는 예시
- basic_ios, basic_istream, basic_ostream, basic_iostream
- 가상 상속과 일반 상속을 섞어 쓰는 경우 어떻게 될까?
- 가상 상속된 것들끼리의 멤버 데이터는 중복되지 않지만, 가상 상속되지 않은 것의 멤버 데이터는 중복 생성된다.
- 즉, 정확한 동작의 관점에서는 public 상속은 항상 반드시 가상 상속으로 하는 것이 맞을 테지만...
가상 상속의 문제점
- 비용이 발생한다.
- 컴파일러는 중복 생성을 막기 위해 꼼수를 사용한다.
- 때문에 가상 상속 한 경우의 클래스 크기가 일반적으로 더 크다.
- 데이터 멤버에 접근하는 속도도 느리다.
- 컴파일러는 중복 생성을 막기 위해 꼼수를 사용한다.
- 초기화 규칙이 훨씬 복잡하고 직관성도 떨어진다.
- 가상 상속의 초기화 규칙
- 초기화가 필요한 가상 기본 클래스로부터 클래스가 파생된 경우
- 이 파생 클래스는 가상 기본 클래스와의 거리에 상관없이 가상 기본 클래스의 초기화를 염두에 두어야 한다.
- 기존의 클래스 계통에 파생 클래스를 새로 추가하는 경우
- 그 파생 클래스는 가상 기본 클래스의 초기화를, 거리에 상관 없이, 떠맡아야 한다.
- 초기화가 필요한 가상 기본 클래스로부터 클래스가 파생된 경우
- 가상 상속의 초기화 규칙
결론
- 구태여 쓸 필요 없으면 가상 상속을 사용하지 말자.
- 굳이 사용해야 한다면, 가상 기본 클래스에는 멤버 데이터를 넣지 말자.
- 가상 기본 클래스의 초기화 규칙으로부터 해방된다.
- 즉, 자바의 interface 개념으로만 사용하자.
728x90
'개발 > Effective C++' 카테고리의 다른 글
[Effective C++] 42. typename의 두 가지 용법 (0) | 2024.07.12 |
---|---|
[Effective C++] 41. 템플릿 프로그래밍 (0) | 2024.07.11 |
[Effective C++] 39. private 상속 (0) | 2024.07.09 |
[Effective C++] 38. 객체 합성 (0) | 2024.07.08 |
[Effective C++] 37. 상속된 함수의 매개변수 기본값 재정의 문제 (0) | 2024.07.05 |
Comments