항목 40 : 다중 상속은 심사숙고해서 사용하자 다중 상속(multiple inheritance: MI)에 대해 꼭 기억하고 있어야 할 점중 하나는, 둘 이상의 기본 클래스로부터 똑같은 이름(함수, typedf 등)을 물려받을 가능성이 생겨 버린다는 점입니다. 다중 상속 때문에 모호성이 생긴다는 것이죠. 아래의 예시를 보시죠. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class BorrowableItem { // 라이브러리로부터 여러분이 가져올 수 있는 어떤 것 public : void checkOut(); // 라이브러리로부터 체크아웃합니다. ... }; class ElectronicGadget { private: bool checkOut() const; // 자체 테..
항목 39 : private 상속은 심사숙고해서 구사하자 private 상속의 의미는 is-implemented-in-terms-of입니다. private 상속은 소프트웨어 설계 도중에는 아무런 의미도 갖지 않으며, 단지 소프트웨어 구현 중에만 의미를 가질 뿐입니다. private를 언제 사용해야 할지에 대한 궁금증을 가질 수 있습니다. 그에 대한 답변은 간단합니다. 할 수 있으면 객체 합성을 사용하고, 꼭 해야 하면 private 상속을 사용하십시오. 꼭 해야 하는 경우는 주로 "비공개 멤버를 접근할 때 혹은 가상 함수를 재정의할 경우" 입니다. private 상속 대신에 public 상속에 객체 합성 조합이 더 자주 즐겨 쓰입니다. 두 가지의 좋은 점이 있기 때문입니다. 첫째, 클래스를 설계하는데 있..
항목 38 : "has-a(...는...를 가짐)" 혹은 "is-implemented-in-terms-of(...는...를 써서 구현됨)"를 모형화할 때는 객체 합성을 사용하자 합성이란, 어떤 타입의 객체들이 그와 다른 타입의 객체들을 포함하고 있을 경우에 성립하는 그 타입들 사이의 관계를 일컫습니다. 포함된 객체들을 모아서 이들을 포함한 다른 객체를 합성한다는 뜻입니다. public 상속의 의미가 "is-a(...는 ...의 일종이다)"라고 공부했었습니다. 객체 합성 역시 의미를 갖고 있습니다. 두 가지의 뜻을 가지고 있는데, "has-a(...는 ...를 가짐)"을 뜻할수도 있고 "is-implemented-in-terms-of(...는 ...를 써서 구현됨)"을 뜻할수도 있습니다. 이렇게 뜻이 두 ..
항목 37 : 어떤 함수에 대해서도 상속받은 기본 매개변수 값은 절대로 재정의하지 말자 C++에서 상속받을 수 있는 함수의 종류는 가상 함수와 비가상 함수 두 가지 입니다. 이들 중 비가상 함수는 언제라도 재정의해서는 안 되는 함수입니다. 객체의 정적 타입은 프로그램 소스 안에 여러분이 놓는 선언문을 통해 그 객체가 갖는 타입입니다. 객체의 동적 타입은 현재 그 객체가 진짜로 무엇이냐에 따라 결정되는 타입입니다. 다시 말해, '이 객체가 어떻게 동작할 것이냐'를 가리키는 타입이 동적 타입이라 하겠습니다. 동적 타입은 프로그램이 실행되는 도중에 바뀔 수 있습니다. 대개 대입문을 통해 바뀌죠. 가상 함수는 동적으로 바인딩됩니다. (가상 함수의) 호출이 일어난 객체의 동적 타입에 따라 어떤 (가상) 함수가 호..
항목 36 : 상속받은 비가상 함수를 파생 클래스에서 재정의하는 것은 절대 금물! 1 2 3 4 5 6 7 class B { public : void mf(); ... }; class D: public B {...}; B나 D, 혹은 mf에 대해 전혀 모르는 상태에서 D 타입의 객체인 x가 다음처럼 있다고 할 때, 1 D x; // x는 D 타입으로 생성된 객체입니다. 다음과 같이 작성한 코드가, 1 2 3 B *pB = &x; // x에 대한 포인터를 얻어냅니다. pB->mf(); // 이 포인터를 통해 mf를 호출합니다. 다음처럼 동작하지 않으면 꽤나 황당할 것입니다. 1 2 3 D *pD = &x; // x에 대한 포인터를 얻어냅니다. pD->mf(); // 이 포인터를 통해 mf를 호출합니다. 황..
항목 35 : 가상 함수 대신 쓸 것들도 생각해 두는 자세를 시시때때로 길러 두자 객체 지향 설계에 대한 여러 가지 방식을 학습해 봅시다. 비가상 인터페이스 관용구를 통한 템플릿 메서드 패턴 사용자로 하여금 public 비가상 멤버 함수를 통해 private 가상 함수를 간접적으로 호출하게 만드는 방법으로, 비가상 함수 인터페이스 관용구라고 많이 알려져 있습니다. 이 관용구는 템플릿 메서드라 불리는 디자인 패턴을 C++ 식으로 구현한 것입니다. 이 관용구에 쓰이는 비가상 함수를 가상 함수의 랩퍼라고 부르기도 합니다. 함수 포인터로 구현한 전략 패턴 가상 함수를 함수 포인터 데이터 멤버로 대체합니다 군더더기 없이 전략 패턴의 핵심만을 보여주는 형태입니다. tr1::function으로 구현한 전략 패턴 가상..
항목 34 : 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자 상속이라는 개념은 두 가지로 나뉩니다. 하나는 함수 인터페이스의 상속이고, 또 하나는 함수 구현의 상속입니다. 특정 클래스가 그 클래스에서 파생된 클래스에 대해 미치는 영향은 절대적입니다. 그 이유는 멤버 함수 인터페이스는 항상 상속되게 되어 있기 때문입니다. 순수 가상 함수는 두 가지 특징을 가지고 있습니다. 첫째, 어떤 순수 가상 함수를 물려받은 구체 클래스가 해당 순수 가상 함수를 다시 선언해야 합니다. 둘째, 순수 가상 함수는 전형적으로 추상 클래스 안에서 정의를 갖지 않습니다. 이 두가지를 조합해보면 하나의 결론이 나옵니다. 순수 가상 함수를 선언하는 목적은 파생 클래스에게 함수의 인터페이스만을 물려주려는 것이다. 단순..
항목 33 : 상속된 이름을 숨기는 일은 피하자 1 2 3 4 5 6 7 8 int x // 전역 변수 void someFunc() { double x; // 지역 변수 std::cin>>x; // 입력을 받아, 지역 변수 x에 새 값을 읽어 넣습니다. } Colored by Color Scripter ------------------------------------------------------------------------------------- | 전역 유효 범위 | | X | | ___________________________| | | someFunc의 유효범위 | | | | | | | -----------------------------------------------------------..
항목 32 : public 상속 모형은 반드시 "is-a(...는 ...의 일종이다)"를 따르도록 만들자 public 상속은 반드시 "is-a" 관계를 뜻해야 합니다. 가상 함수의 의미는 "인터페이스가 상속되어야 한다"인 반면, 비가상 함수의 의미는 "인터페이스와 구현이 둘 다 상속 되어야 한다"입니다. public 상속은 기본 클래스 객체가 가진 모든 것들이 파생 클래스 객체에도 그대로 적용된다고 단정하는 상속입니다. 클래스들 사이에 맺을 수 있는 관계로 is-a 관계만 있는 것은 아닙니다. 두 가지가 더 있는데, 하나는 "has-a(...는...를 가짐)"이고 또 하나는 "is-implemented-in-terms-of(...는...를 써서 구현됨)"입니다. 꼭 잊지 말아야 할 것! public 상속..