티스토리 뷰
7. 템플릿과 일반화 프로그래밍 (1)
항목 41 : 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타임 다형성부터
명시적 인터페이스와 런타임 다형성의 예
class Widget
{
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);
};
// w는 명시적 인터페이스
void doProcessing(Widget& w)
{
if(w.size() > 10 && w != someNastyWidget)
{
Widget temp(w);
// 가상 함수의 호출은 런타임 다형성에 의해 호출
temp.normalize();
temp.swap(w);
}
}
암시적 인터페이스와 컴파일 다형성의 예
template<typename T>
// T는 암시적 인터페이스
void doProcessing(T& w)
{
// operator > 및 operator!= 함수 호출 시 컴파일 타임에 템플릿 인스턴스화 발생
// 컴파일 다형성의 예
if(w.size() > 10 && w != someNastyWidget)
{
T temp(w);
temp.normalize();
temp.swap(w);
}
}
- 클래스 및 템플릿은 모두 인터페이스와 다형성을 지원합니다.
- 클래스의 경우, 인터페이스는 명시적이며 함수의 시그니처를 중심으로 구성되어 있습니다. 다형성은 프로그램 실행 중에 가상 함수를 통해 나타납니다.
- 템플릿 매개변수의 경우, 인터페이스는 암시적이며 유효 표현식에 기반을 두어 구성됩니다. 다형성은 컴파일 중에 템플릿 인스턴스화와 오버로딩 모호성 해결을 통해 나타납니다.
항목 42 : typename의 두 가지 의미를 제대로 파악하자
typename 키워드는 중첩 의존 이름만 식별하는 데 써야 합니다.
template<typename C> // typename 쓸 수 있음("class"와 같은 의미)
void if(const C& container, // typename 쓰면 안됨
typename C::iterator iter); // typename 꼭 써야 됨
예외
중첩 의존 타입 이름이 기본 클래스의 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로서 있을 경우 typename을 붙여 주면 안됩니다.
template<typename T>
// 상속되는 기본 클래스 리스트: typename 쓰면 안됨
class Derived: public Base<T>::Nested
{
public:
// 멤버 초기화 리스트에 있는 기본 클래스 식별자: typename 쓰면 안됨
explicit Derived(int x) : Base<T>::Nested(x)
{
// 중첩 의존 타입 이름이며 기본 클래스 리스트에도 없고
// 멤버 초기화 리스트의 기본 클래스 식별자도 아님: typename 필요
typename Base<T>::Nested temp;
...
}
...
};
- 템플릿 매개변수를 선언할 때, class 및 typename은 서로 바꾸어 써도 무방합니다.
- 중첩 의존 타입 이름을 식별하는 용도에는 반드시 typename을 사용합니다. 단. 중첩 의존 이름이 기본 클래스 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로 있는 경우에는 예외입니다.
항목 43 : 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아 두자
예) 컴파일 타임에 어떤 회사로 암호화 또는 비암호화 메시지를 보낼지 결정하는 응용프로그램
class CompanyA{
public:
...
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
...
};
class CompanyB{
public:
...
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
...
};
// 메시지 생성에 사용되는 정보를 담기 위한 클래스
class MsgInfo{ ... };
template<typename Company>
class MsgSender{
public:
// 생성자, 소멸자 등등
...
void sendClear(const MsgInfo& info)
{
std::string msg;
// info로부터 msg를 만듬
Company c;
c.sendCleartext(msg);
}
void sendSecret(const MsgInfo& info)
{ ... }
};
문제
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>{
public:
...
void sendClearMsg(const MsgInfo& info)
{
// "메시지 전송 전" 정보를 로그에 기록
sendClear(info); // 기본 클래스의 함수를 호출하는데, 이 코드는 컴파일되지 않음
// "메시지 전송 후" 정보를 로그에 기록
}
...
};
해결1
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>{
public:
...
void sendClearMsg(const MsgInfo& info)
{
this->sendClear(info);
}
};
해결2
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>{
public:
// 컴파일러에게 sendClear 함수가 기본 클래스에 있다고 가정하라고 알려줌
using MsgSender<Company>::sendClear;
...
void sendClearMsg(const MsgInfo& info)
{
this->sendClear(info);
}
};
해결3
template<typename Company>
class LoggingMsgSender: public MsgSender<Company>{
public:
...
void sendClearMsg(const MsgInfo& info)
{
MsgSender<Company>::sendClear(info);
}
};
- 파생 클래스 템플릿에서 기본 클래스 템플릿의 이름을 참조할 때는, "this->"를 접두사로 붙이거나 기본 클래스 한정문을 명시적으로 써 주는 것으로 해결합시다.
- James Song
'Books_tech' 카테고리의 다른 글
제럴드 와인버그의 통찰력 <프로그래밍 심리학> (1/3) (0) | 2016.01.05 |
---|---|
다시 읽는 <Effective C++> Chapter 7 요약 (2) (0) | 2015.12.19 |
다시 읽는 <Effective C++> Chapter 6 요약 (3) (0) | 2015.12.01 |
다시 읽는 <Effective C++> Chapter 6 요약 (2) (0) | 2015.11.24 |
다시 읽는 <Effective C++> Chapter 6 요약 (1) (0) | 2015.11.20 |
- #팀개발
- 객체 지향 설계
- #알고리즘
- Effective C++
- Effective Modern C++
- #ModernCPP
- #uwp
- #csharp
- #build2016
- #techdays2015
- #cpp
- #마이클페더스
- #스콧마이어스
- #cplusplus
- #클린코드
- #임백준
- #scottmeyers
- #자녀교육
- #레거시코드
- #mva
- 상속
- #세미나
- #EffectiveModernCpp
- Scott Meyers
- 책
- #ndc
- #프로그래밍심리학
- #로버트마틴
- #코드최적화
- #제럴드와인버그
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |