항목 43 : 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아 두자 1 2 3 4 5 6 template class MsgSender{ public : ... void sendSecret(const MsgInfo& info){...} }; Colored by Color Scripter 괄호 안에 아무것도 없는 template의 뜻은 "이건 템플릿도 아니고 클래스도 아니다"라는 것입니다. 위의 코드는 MsgSender 템플릿을 템플릿 매개변수가 CompanyZ일 때 쓸 수 있도록 특수화한 버전입니다. 이러한 특수화는 완전 템플릿 특수화라고 합니다. MsgSender 템플릿이 CompanyZ 타입에 대해 특수화되었고, 이때 이 템플릿의 매개변수들이 하나도 빠짐없이 구체적인 타입으로 정해진 ..
항목 42 : typename의 두 가지 의미를 제대로 파악하자 1 2 3 4 5 6 7 8 9 10 11 12 template void print2nd(const C& container) { if(container.size() >= 2) { C::const_iterator iter(container.begin()); ++iter; int value = *iter; std::cout = 2) { typename C::const_iterator iter(container.begin()); ... } } Colored by Color Scripter 어느 때이든지 템플릿 안에서 중첩 의존 이름을 참조할 경우에는, 그 이름 앞에 typename 키워드를 붙여 주는 것을 잊지 마세요. "typename은 중첩 ..
항목 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 : 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자 상속이라는 개념은 두 가지로 나뉩니다. 하나는 함수 인터페이스의 상속이고, 또 하나는 함수 구현의 상속입니다. 특정 클래스가 그 클래스에서 파생된 클래스에 대해 미치는 영향은 절대적입니다. 그 이유는 멤버 함수 인터페이스는 항상 상속되게 되어 있기 때문입니다. 순수 가상 함수는 두 가지 특징을 가지고 있습니다. 첫째, 어떤 순수 가상 함수를 물려받은 구체 클래스가 해당 순수 가상 함수를 다시 선언해야 합니다. 둘째, 순수 가상 함수는 전형적으로 추상 클래스 안에서 정의를 갖지 않습니다. 이 두가지를 조합해보면 하나의 결론이 나옵니다. 순수 가상 함수를 선언하는 목적은 파생 클래스에게 함수의 인터페이스만을 물려주려는 것이다. 단순..