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

Chapter7~9 ppt

  • 1.
  • 2.
    항목 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++표준화 위원회와 밀접하고 영향력있는 관 꼐 유지 • 라이브러리 승인 심사는 공개 동료 심사에 기반을 두고 있음 • 부스트 라이브러리 군단은 엄청나게 유용하게 쓰임 • 문자열 및 텍스트처리, 컨테이너, 함수 객체 및 고차 프로 그래밍, 일반화 프로그래밍, 템플릿 메타 프로그래밍, 수 학 및 수치조작, 정확성 유지 및 테스트, 자료구조, 타 언 어와의 연동 지원, 메모리 등등