항목 41 :템플릿 프로그래밍은 암
시적 인터페이스와 컴파일 타임
형성부터
• 객체 지향에서 중요한 점은 명
시적 인터페이스와 런타임 다
형성
• 인터페이스를 소스코드에서
찾으면 이것이 어떤 형태인지
를 확인할 수 있는데 이런 인
터페이스를 명시적 인터페이
스
• 소스 코드에 명시적으로 드러
나는 인터페이스
• class Widget{};
• void Do( Widget&
w){
};
• w타입이 무엇인지
코드를 통해 알 수
있다. 즉 명시적 인
터페이스
3.
암시적 인터페이스와 컴파일타
임 다형성
• 암시적 인터페이스와 컴파일
타임 다형성은 템플릿 일반화
프로그래밍에서 매우 많이 활
용
• 템플릿의 인스턴스화가 일어
나는 시점은 컴파일 도중
• 이것을 가리켜 컴파일 타임 다
형성
• template<typename T>
void Do(T& w)
{
…
}
이 함수에서는 w 의 타
입은 T가 된다.
4.
항목 41 정리
•클래스 및 템플릿은 모두 인터페이스와 다형성
을 지원함
• 클래스의 경우, 인터페이스는 명시적이며 함수
의 시그너처를 중심으로 구성되어 있습니다. 다
형성은 프로그램 실행 중에 가상 함수를 통해 나
타남
• 템플릿 매개변수의 경우 인터페이스는 암시적이
며 유효 표현식에 기반을 두어 구성
• 다형성은 컴파일 중에 테믈릿 인스턴스화와 함
수 오버로딩 모호성 해결을 통해 나타남
5.
항목 42 :typename의 두가지 의
미
• 템플릿의 타입 매개변수를 선
언할 때는 class와 typename
의 뜻이 완전히 똑같다
• 템플릿 매개변수에 종속된 것
을 가리켜 의존 이름이라고 한
다.
• 템플릿 매개변수가 어떻든 상
관없는 타입 이름을 비의존 이
름이라고 한다.
• template<class T>class
W;
• template<typename T>
class W;
• 둘은 차이가 없다.
• void Pro(const C& con){
C::const_iterator
iter(con.begin());
int value ;
}
• iter는 템플릿 매개변수 C에
의존적이다.
value는 C에 의존적이지 않
다.
6.
typename의 예외
• typename키워드는 중첩 이름 의존만 식별하는
데 써야 한다. 그 외 이름은 type-name을 가져선
안 된다.
• 예외적으로 중첩 의존 타입 이름이 기본 클래스
의 리스트에 있거나 멤버 초기화 리스트 내의 기
본 클래스 식별자로서 있을 경우에는 typename
을 붙여 주면 안 된다.
7.
항목 42 정리
•템플릿 매개변수를 선언할 때, class 및
typename은 서로 바꾸어 써도 무방
• 중첩 의존 타입 이름을 식별하는 용도에는 반드
시 typename을 사용, 단 중첩 의존 이름이 기본
클래스 리스트에 있거나 멤버 초기화 리스트 내
의 기본 클래스 식별자로 있는 경우에는 예외
8.
항목 43 :템플릿으로 만들어진
기본 클래스 안의 이름에 접근하
는 방법
• 컴파일러가 클래스 템플릿의
정의와 마주칠 때 컴파일러는
대체 이 클래스가 어디서 파생
된 것인지를 모름
• 옆에 예제에서 Company는 템
플릿 매개변수이고, 이 템플릿
매개변수는 나중까지 무엇이
될지 알 수 없음
• Company가 정확히 무엇인지
도 모르는데 사용할 수는 없음
• template<typename Company>
class Msg{
public:
..
void sendClear(){ … };
};
…
class Log: public Msg{
sendClear();
};
• 다음은 컴파일이 되지 않는 코드
이다.
9.
문제 해결 방법
•기본 클래스 함수에 대한 호출문 앞에 “this->”를
붙인다.
this->sendClear();
• using 선은을 통해 해결
using Msg<Company>::sendClear;
즉 컴파일러에서 sendClear가 기본 클래스에 있
다고 가정하라 고 알려줌
10.
항목 43 정리
•파생 클래스 템플릿에서 기본 클래스 템플릿의
이름을 참조할 때는 , “this->”를 접두사로 붙인다
• using 키워드를 이용하여 함수를 선언( 이 함수
는 기본 클래스에 존재한다) 하여 기본 클래스 한
정문을 명시적으로 써주는 것으로 해결 가능
11.
항목 44 :매개변수에 독립적인
코드는 템플릿으로부터 분리시키
자
• 템플릿은 코딩 시간 절약, 코
드 중복 회피의 이점이 있다.
• 그러나 아무 생각 없이 템플릿
을 사용하면 코드 비대화를 초
래할 수 있다.
-> 성능 저하 야기
• 즉 템플릿을 구성할 때도 코드
중복을 피해야만 한다.
• vector<int>
• vector<long>
• 위의 두 멤버 함수는 서로 똑
같게 나올 수 있다. 즉 딱 코드
비대화가 되기 쉽다.
12.
문제 해결 방법
•포인터 타입을 매개변수로 취하는 동일 계열의
템플릿들 ( list<int*>, list<const int*>,
list(SquareMatrix<long,3>*>등 은 이진 수준에서
만 보면 멤버 함수 집합을 한 벌만 써도 가능
• 기술적으로 타입제약이 엄격한 포인터(T* 포인
터)를 써서 동작하는 멤버함수를 구현할 때는 하
단에서 타입미정 포인터로 동작하는 버전을 호
출
13.
항목 44 정리
•템플릿을 사용하면 비슷비슷한 클래스와 함수가
여러 벌 만들어진다 따라서 템플릿 배개변수에
종속되지 않은 템플릿 코드는 비대화의 원인
• 비타입 템플릿 매개변수로 생기는 코드 비대화
의 경우, 테믈릿 매개변수를 함수매개변수 혹은
클래스 데이터 멤버로 대체하여 비대화를 없앨
가능성 있음
• 타입 매개변수로 생기는 코드 비대화의 경우, 동
일한 이진 표현구조를 가지고 인스턴스화되는
타입들이 한 가지 함수 구현을 공유하게 만듦으
로 비대화를 감소시킬 수 있음
14.
항목 45 :호환되는 모든 타입을
받아들이는 데는 템플릿이 답이
다
• 타입 변환을 스마트 포인터를
써서 하려면 무척 까다롭다.
• 대신 기본 포인터는 암시적 변
환을 지원하여 파생클래스 ㅂ
포인터가 암시적으로 기본 클
래스 포인터로 변환가능하다
• 즉 기본 포인터가 타입 변환에
조금 더 용이하다
• class Top { … };
class Middle : public Top { … };
class Bottom: public Middle { … };
Top *pt1 = new Middle;
Top *pt2 = new Bottom
즉, 기본 포인터를 이용하면 암시
적 변환을 통해 기본클래스 포인
터로 파생 클래스를 가리킬 수 있
음
15.
생성자를 만들어내는 템플
릿을쓰자
• 생성자 템플릿은 멤버 함수 템플릿의 한가지 예이다
template<typename T>
class SmartPtr {
public:
template<typename U >
SmartPtr( const SmartPtr<U>& other);
};
모든 T 타입 및 모든 U 타입에 대해서 SmartPtr<T>객체
가 SmartPtr<U>로 부터 생성될 수 있음
• 이런 꼴의 생성자를 가리켜 일반화 복사 생성자라 한다
• 기본 포인터처럼 SmartPointer활용 가능
16.
항목 45 정리
•호환되는 모든 타입을 받아들이는 멤버 함수를
만들려면 멤버 함수 템플릿을 사용하다자
• 일반화된 복사 생성 연산과 일반화된 대입 연산
을 위해 멤버 템플릿을 선언했다 하더라도, 보통
의 복사 생성자와 복사 대입 연산자는 여전히 직
접 선언해야 한다
17.
항목 46 :타입 변환이 바람직할 경우
에는 비멤버 함수를 클래스 템플릿
안에 정의
• 예제를 보면 컴파일이 되지 않
는다
• 템플릿 버전의 Rational에는
실제로 어떤 함수를 호출하려
는지에 대해 컴파일로서는 알
수 없다 단지 컴파일러는
operator*라는 이름의 템플릿
으로부터 인스턴스화 할 함수
를 결정하기 위해 노력
• 하지만 컴파일러는 T를 알아
야 컴파일이 가능
• template<typename T>
class Rational {
public:
Rational(const T& numerator = 0, const T&
denominator = 1);
…
};
template<typename T>
const Rational<T> operator*(const Rational<T>&
lhs, const Rational<T>& rhs>
Rational<int> oneHalf(1,2);
Rational<int> result = oneHalf *2;
이 코드는 컴파일이 되지 않는다.
18.
operator* 함수의 본문을선
언부와 붙이자• class Rational {
public:
…
friend const Rational operator* (const
Rational& lhs, const Rational& rhs) {
return Rational( lhs.numerator() *
rhs.numerator(),
lhs.denominator() *
rhs.denominator() );
• 프렌드 함수를 선언했지만 클래스의 public영역이
아닌 부분에 접근하는 것과 프렌드 권한은 아무런
상관이 없음
19.
항목 46 정리
•모든 매개변수에 대해 암시적 타입 변환을 지원
하는 템플릿과 관계가 있는 함수를 제공하는 클
래스 템플릿을 만들려고 한다면, 이런 함수는 클
래스 템플릿 안에 프렌드 함수로서 정의
20.
항목 47 :타입에 대한 정보가
필요하다면 특성정보 클래스를
사용하자
• 특성정보란 컴파일 도중에 어떤 주어진 타입의
정보를 얻을 수 있게 하는 객체를 지칭하는 개념
• 특성정보는 문법구조나 키워드가 아니다
• C++ 프로그래머들의 구현 기법이며 관례
21.
특성정보 클래스의 설계및
구현 방법
• 다른 사람이 사용하도록 열어 주고 싶은 타입 관
련 정보를 확인( 예를 들어, 반복자라면 반복자
범주 등)
• 그 정보를 식별하기 위한 이름을 선택
• 지원하고자 하는 타입 관련 정보를 담은 템플릿
및 그 템플릿의 튻화버전을 제공
22.
항목 47 정리
•특성정보 클래스는 컴파일 도중에 사용할 수 있
는 타입 관련 정보를 만들어냄
• 특성정보 클래스는 템플릿 및 템플릿 특수 버전
을 사용하여 구현
• 함수 오버로딩 기법과 결합하여 특성정보 클래
스를 사용하면 컴파일 타임에 결정되는 타입별
if…else점검문을 구사 가능
23.
항목 48 :템플릿 메타프로그래밍
• 템플릿 메타프로그래밍은 컴파
일 도중에 실행되는 템플릿 기
반의 프로그램
• 다른 방법으로는 까다롭거나
불가능한 일을 굉장히 쉽게 할
가능성이 생김
• 컴파일이 진행되는 동안에 실
행되기 때문에, 기존 작업을 런
타임 영역에서 컴파일 타임 영
역으로 전환 가능
• 모든 면에서 효율이 좋을 가능
성이 있음
-> 컴파일 타임에 동작을 해서
실행코드가 작아지고 실행시간
도 짧아짐, 메모리도 적게먹음
• template<unsigned n>
struct Factorial {
enum { value = n* Factorial<n-1>::value);
};
template<>
struct Factorial<0>{
enum { value = 1 };
};
템플릿 메타프로그래밍으로 factorial 작성
Factorial<5>::value 는 120을 런타임 계산
없이 출력!
24.
메타프로그래밍이 좋은
예
• 치수단위의 정확성 확인 : 과학기술 분야의 응용
프로그램을 만들 때 유리
• 행렬 연산의 최적화
• 맞춤식 디자인 패턴 구현의 생성 - 디자인 패턴에
대한 구현방법이 여러가지일 때 기반을 설계할
수 있음
25.
항목 48 정리
•템플릿 메타프로그래밍은 기존 작업을 런타임에
서 컴파일 타임으로 전환하는 효과를 냄. 따라서
TMP를 쓰면 선행 에러 탐지와 높은 런타임 효율
을 가질 수 있음
• TMP는 정책 선택의 조합에 기반하여 사용자 정
의 코드를 생성하는 데 쓸 수 있으며, 또한 특정
타입에 대해 부적절한 코드가 만들어지는 것을
막는 데도 쓸 수 있음
26.
항목 49 :new처리자의 동작 원
리를 제대로 이해하자
• 사용자가 부탁한 만큼의 메모
리를 할당해 주지 못하면
operator new 는 충분한 메모
리를 찾아낼 때까지 new 처리
자를 되풀이해서 호출
• Class X{
public:
static void outOfMemory();
…
};
Class Y{
public:
static void outOfMemory();
…
};
X* p1 = new X;
Y* p2 = new Y;
메모리 할당이 실패했을 경우 두 경우, 각 클래스는
OutofMemory를 호출
27.
new 처리자 함수처리시 주
의할 점
• 사용할 수 있는 메모리를 더 많이 확보
• 다른 new 처리자를 설치
• new 처리자의 설치를 제거 - null pointer를 넘김
• 예외를 던짐 - bad_alloc등의 예외를 던짐
• 복귀하지 않음 - abort 혹은 exit ghcnf
28.
항목 49 정리
•set_new_handler 함수를 쓰면 메모리 할당 요청
이 만족되지 못했을 때 호출되는 함수를 지정할
수 있음
• 예외불가 new는 영향력이 제한되어 있음 - 메모
리 할당 자체에만 적용되기 때문. 이후에 호출되
는 생성자에는 얼마든지 예외를 던질 수 있음
29.
항목 50 :new및 delete를 언제
바꿔야 좋은 소리를 들을지 파악
• 컴파일러가 준 operator new와 operator delete를 바꾸고싶은 이유
?
• 잘못된 힙 사용을 탐지하기 위해
• 효율을 향상시키기 위해
• 동적 할당 메모리의 실제 사용에 관한 통계 정보를 수집하기 위해
30.
다시한번 필요한 조건체
크• 잘못된 힙 사용을 탐지하기 위해
• 동적 할당 메모리의 실제 사용에 관한 통계 정보를 수
집하기 위해할당 및 해제 속력을 높이기 위해
• 기본 메모리 관리자의 공간 오버헤드를 줄이기 위해
• 적당히 타협한 기본 할당자의 바이트 정렬 동작을 보장
하기 위해
• 임의의 관계를 맺고 있는 객체들을 한 군데애 나란히
모아 놓기 위해
• 그때그떄 원하는 동작을 수행하도록 하기 위해
31.
항목 50 정리
•개발자가 스스로 사용자 정의 new 및 delete를
작성하는 데는 여러 가지 나름대로 타당한 이유
가 있다 여기에는 성능을 향상시키려는 목적, 힙
사용시의 에러를 디버깅하려는 목적, 힙사용 정
보를 수집하려는 목적 등이 포함
32.
항목 51 :new 및 delete를 작성할
때 따라야 할 기존의 관례를 잘 알
아두자• operator new의 반환 값 부분은 요청된 메모리를 마련해 줄 수 있
으면 그 메모리에 대한 포인터를 반환, 반환할 수 없으면 bad_alloc
호출
• operator new 멤버 함수는 파생 클래스 쪽으로 상속이 되는 함수
• 배열안에 몇 개 의 객체가 들어갈 지 계싼하는 것 어려움 -> 객체
하나가 얼마나 큰지를 확정할 방법이 없음
• operator new[]에 넘어가는 size_t 타입의 인자는 객체들을 담기에
딱 맞는 메모리 양보다 더 많게 설정되어 있을 수도 있음
• 가상 소멸자가 없는 기본 클래스로부터 파생된 클래스의 객체를
삭제하려고 할 경우에는 operator delete로 C++가 넘기는 size_t값
이 엉터리 일 수 있음
33.
항목 51 정리
•관레적으로 operator new 함수는 메모리 할당을
반복해서 시도하는 무한 루프를 가져야 하고, 메
모리 할당 요구를 만족시킬 수 없을 때 new처리
자를 호출해야 하며, 0바이트에 대한 대책도 있
어야 함
• 클래스 전용 버전은 자신이 할당하기로 예정된
크기보다 더 큰 메모리 블록에 대한 요구도 처리
• operator delete 함수는 널 포인터가 들어왔을 떄
아무 일도 하지 않아야 함
34.
항목 52 :위치지정 new를 작성
한다면 위치지정 delete도 같이
준비하자
• 런타임 시스템이 해주어야 하
는 일은 자신이 호출한
operator new 함수와 짝이 되
는 버전의 operator delete함
수를 호출
• 런타임 시스템은 호출된
operator new가 받아들이는
매개변수의 개수 및 타입이 똑
같은 버전의 operator delete
를 찾고 찾아냈으면 호출.
• 즉 짝을 이루는 시그너처
• Widget *pw = new Widget
이 실행이 되면 우선 메모리 할당을
위해 operator new가 호출되고, 그 뒤
를 이어 Widget의 기본 생성자 호출
이때 기본 생성자 호출을 하면서 예외
가 발생했다고 하면 이 메모리는 해제
를 해주어야한다!
-> C++ 런타임 시스템에서 맡아서 함
35.
위치지정 주의 할점
• 매개변수를 추가로 받는 new를 위치지정 이라고 한다
• operator delete역시 이 위치지정과 똑같은 시그너처를 가진 것이
어야한다
-> 그렇지 않으면 어떤 delete를 호출하는지 갈팡질팡 하기 때문이
다
• 따라서 위치지정을 하면 그에 맞는 delete도 작성해야 한다
36.
항목 52 정리
•operator new함수의 위치지정 버전을 만들 떄는
이 함수와 짝을 이루는 위치지정 버전의
operator delete함수도 꼭 만들어야 함 -> 메모리
누출 위험성
• new 및 delete의 위치지정 버전을 선언할 때는,
의도한 바도 아닌데 이들의 표준 버전이 가려지
는 일이 생기지 않도록 주의
37.
항목 53: 컴파일러경고를
지나치지 말자
• 컴파일러 경고를 쉽게 지나치지 말아야 한다 ->
후에 분명히 문제가 생기는 일이 십중팔구
• 컴파일러 경고에 너무 의지하는 일을 하지 말 것
-> 컴파일러마다 경고를 내는 부분이 다르기 때
문이다.
38.
항목 54: TR1을포함한 표준 라
이브러리 구성요소와 편안한 친
구가 되자
• 표준 템플릿 라이브러리 (STL) : 컨테이너, 반복
자, 알고리즘 ,함수객체 등을 담고 있음
• iostream, 국제화 지원, 수치 처리 지원
• 예외 클래스 계통 지원
• 이러한 라이브러리 구성요소를 사용하여 코딩의
효율성을 키우자
39.
항목 55: 부스트를늘 여러
분 가까이에• 부스트는 C++ 개발자들의 단체이자 무료 다운로드가 가
능한 C++라이브러리 집합을 동시에 일컫는 고유명사 ->
웹사이트 http://boost.org에 방문하여 만날 수 있음
• 부스트는 C++표준화 위원회와 밀접하고 영향력있는 관
꼐 유지
• 라이브러리 승인 심사는 공개 동료 심사에 기반을 두고
있음
• 부스트 라이브러리 군단은 엄청나게 유용하게 쓰임
• 문자열 및 텍스트처리, 컨테이너, 함수 객체 및 고차 프로
그래밍, 일반화 프로그래밍, 템플릿 메타 프로그래밍, 수
학 및 수치조작, 정확성 유지 및 테스트, 자료구조, 타 언
어와의 연동 지원, 메모리 등등