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