39
Effective C++ 정정 Chap7~9 131054 정정정

Chapter7~9 ppt

Embed Size (px)

Citation preview

Page 1: Chapter7~9 ppt

Effective C++ 정리Chap7~9

131054 이인재

Page 2: Chapter7~9 ppt

항목 41 : 템플릿 프로그래밍은 암시적 인터페이스와 컴파일 타임 형성부터

• 객체 지향에서 중요한 점은 명시적 인터페이스와 런타임 다형성

• 인터페이스를 소스코드에서 찾으면 이것이 어떤 형태인지를 확인할 수 있는데 이런 인터페이스를 명시적 인터페이스

• 소스 코드에 명시적으로 드러나는 인터페이스

•class Widget{};

•void Do( Widget& w){};

•w 타입이 무엇인지 코드를 통해 알 수 있다 . 즉 명시적 인터페이스

Page 3: Chapter7~9 ppt

암시적 인터페이스와 컴파일 타임 다형성

• 암시적 인터페이스와 컴파일 타임 다형성은 템플릿 일반화 프로그래밍에서 매우 많이 활용

• 템플릿의 인스턴스화가 일어나는 시점은 컴파일 도중

• 이것을 가리켜 컴파일 타임 다형성

• template<typename T>void Do(T& w){ …}

이 함수에서는 w 의 타입은 T 가 된다 .

Page 4: Chapter7~9 ppt

항목 41 정리• 클래스 및 템플릿은 모두 인터페이스와 다형성을

지원함

• 클래스의 경우 , 인터페이스는 명시적이며 함수의 시그너처를 중심으로 구성되어 있습니다 . 다형성은 프로그램 실행 중에 가상 함수를 통해 나타남

• 템플릿 매개변수의 경우 인터페이스는 암시적이며 유효 표현식에 기반을 두어 구성

• 다형성은 컴파일 중에 테믈릿 인스턴스화와 함수 오버로딩 모호성 해결을 통해 나타남

Page 5: Chapter7~9 ppt

항목 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 에 의존적이지 않다 .

Page 6: Chapter7~9 ppt

typename 의 예외

• typename 키워드는 중첩 이름 의존만 식별하는 데 써야 한다 . 그 외 이름은 type-name 을 가져선 안 된다 .

• 예외적으로 중첩 의존 타입 이름이 기본 클래스의 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로서 있을 경우에는 typename 을 붙여 주면 안 된다 .

Page 7: Chapter7~9 ppt

항목 42 정리

• 템플릿 매개변수를 선언할 때 , class 및 typename 은 서로 바꾸어 써도 무방

• 중첩 의존 타입 이름을 식별하는 용도에는 반드시 typename 을 사용 , 단 중첩 의존 이름이 기본 클래스 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로 있는 경우에는 예외

Page 8: Chapter7~9 ppt

항목 43 : 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법

• 컴파일러가 클래스 템플릿의 정의와 마주칠 때 컴파일러는 대체 이 클래스가 어디서 파생된 것인지를 모름

• 옆에 예제에서 Company 는 템플릿 매개변수이고 , 이 템플릿 매개변수는 나중까지 무엇이 될지 알 수 없음

• Company 가 정확히 무엇인지도 모르는데 사용할 수는 없음

• template<typename Company>class Msg{public:..void sendClear(){ … };};

class Log: public Msg{ sendClear();};

• 다음은 컴파일이 되지 않는 코드이다 .

Page 9: Chapter7~9 ppt

문제 해결 방법• 기본 클래스 함수에 대한 호출문 앞에 “ this->” 를

붙인다 .this->sendClear();

• using 선은을 통해 해결using Msg<Company>::sendClear; 즉 컴파일러에서 sendClear 가 기본 클래스에 있다고 가정하라 고 알려줌

Page 10: Chapter7~9 ppt

항목 43 정리

• 파생 클래스 템플릿에서 기본 클래스 템플릿의 이름을 참조할 때는 , “this->” 를 접두사로 붙인다

• using 키워드를 이용하여 함수를 선언 ( 이 함수는 기본 클래스에 존재한다 ) 하여 기본 클래스 한정문을 명시적으로 써주는 것으로 해결 가능

Page 11: Chapter7~9 ppt

항목 44 : 매개변수에 독립적인 코드는 템플릿으로부터 분리시키자

• 템플릿은 코딩 시간 절약 , 코드 중복 회피의 이점이 있다 .

• 그러나 아무 생각 없이 템플릿을 사용하면 코드 비대화를 초래할 수 있다 . -> 성능 저하 야기

• 즉 템플릿을 구성할 때도 코드 중복을 피해야만 한다 .

• vector<int>

• vector<long>

• 위의 두 멤버 함수는 서로 똑같게 나올 수 있다 . 즉 딱 코드 비대화가 되기 쉽다 .

Page 12: Chapter7~9 ppt

문제 해결 방법

• 포인터 타입을 매개변수로 취하는 동일 계열의 템플릿들 ( list<int*>, list<const int*>, list(SquareMatrix<long,3>*> 등 은 이진 수준에서만 보면 멤버 함수 집합을 한 벌만 써도 가능

• 기술적으로 타입제약이 엄격한 포인터 (T* 포인터 )를 써서 동작하는 멤버함수를 구현할 때는 하단에서 타입미정 포인터로 동작하는 버전을 호출

Page 13: Chapter7~9 ppt

항목 44 정리• 템플릿을 사용하면 비슷비슷한 클래스와 함수가 여러

벌 만들어진다 따라서 템플릿 배개변수에 종속되지 않은 템플릿 코드는 비대화의 원인

• 비타입 템플릿 매개변수로 생기는 코드 비대화의 경우 , 테믈릿 매개변수를 함수매개변수 혹은 클래스 데이터 멤버로 대체하여 비대화를 없앨 가능성 있음

• 타입 매개변수로 생기는 코드 비대화의 경우 , 동일한 이진 표현구조를 가지고 인스턴스화되는 타입들이 한 가지 함수 구현을 공유하게 만듦으로 비대화를 감소시킬 수 있음

Page 14: Chapter7~9 ppt

항목 45 : 호환되는 모든 타입을 받아들이는 데는 템플릿이 답이다

• 타입 변환을 스마트 포인터를 써서 하려면 무척 까다롭다 .

• 대신 기본 포인터는 암시적 변환을 지원하여 파생클래스 ㅂ포인터가 암시적으로 기본 클래스 포인터로 변환가능하다

• 즉 기본 포인터가 타입 변환에 조금 더 용이하다

• class Top { … };class Middle : public Top { … };class Bottom: public Middle { … };

Top *pt1 = new Middle;Top *pt2 = new Bottom

즉 , 기본 포인터를 이용하면 암시적 변환을 통해 기본클래스 포인터로 파생 클래스를 가리킬 수 있음

Page 15: Chapter7~9 ppt

생성자를 만들어내는 템플릿을 쓰자

• 생성자 템플릿은 멤버 함수 템플릿의 한가지 예이다template<typename T>class SmartPtr {public: template<typename U > SmartPtr( const SmartPtr<U>& other);};모든 T 타입 및 모든 U 타입에 대해서 SmartPtr<T>객체가 SmartPtr<U> 로 부터 생성될 수 있음

• 이런 꼴의 생성자를 가리켜 일반화 복사 생성자라 한다

• 기본 포인터처럼 SmartPointer 활용 가능

Page 16: Chapter7~9 ppt

항목 45 정리

• 호환되는 모든 타입을 받아들이는 멤버 함수를 만들려면 멤버 함수 템플릿을 사용하다자

• 일반화된 복사 생성 연산과 일반화된 대입 연산을 위해 멤버 템플릿을 선언했다 하더라도 , 보통의 복사 생성자와 복사 대입 연산자는 여전히 직접 선언해야 한다

Page 17: Chapter7~9 ppt

항목 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;

이 코드는 컴파일이 되지 않는다 .

Page 18: Chapter7~9 ppt

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영역이 아닌 부분에 접근하는 것과 프렌드 권한은 아무런 상관이 없음

Page 19: Chapter7~9 ppt

항목 46 정리

• 모든 매개변수에 대해 암시적 타입 변환을 지원하는 템플릿과 관계가 있는 함수를 제공하는 클래스 템플릿을 만들려고 한다면 , 이런 함수는 클래스 템플릿 안에 프렌드 함수로서 정의

Page 20: Chapter7~9 ppt

항목 47 : 타입에 대한 정보가 필요하다면 특성정보 클래스를 사용하자

• 특성정보란 컴파일 도중에 어떤 주어진 타입의 정보를 얻을 수 있게 하는 객체를 지칭하는 개념

• 특성정보는 문법구조나 키워드가 아니다

• C++  프로그래머들의 구현 기법이며 관례

Page 21: Chapter7~9 ppt

특성정보 클래스의 설계 및 구현 방법

• 다른 사람이 사용하도록 열어 주고 싶은 타입 관련 정보를 확인 ( 예를 들어 , 반복자라면 반복자 범주 등 )

• 그 정보를 식별하기 위한 이름을 선택

• 지원하고자 하는 타입 관련 정보를 담은 템플릿 및 그 템플릿의 튻화버전을 제공

Page 22: Chapter7~9 ppt

항목 47 정리• 특성정보 클래스는 컴파일 도중에 사용할 수 있는

타입 관련 정보를 만들어냄

• 특성정보 클래스는 템플릿 및 템플릿 특수 버전을 사용하여 구현

• 함수 오버로딩 기법과 결합하여 특성정보 클래스를 사용하면 컴파일 타임에 결정되는 타입별 if…else점검문을 구사 가능

Page 23: Chapter7~9 ppt

항목 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 을 런타임 계산없이 출력 !

Page 24: Chapter7~9 ppt

메타프로그래밍이 좋은 예

• 치수 단위의 정확성 확인 : 과학기술 분야의 응용 프로그램을 만들 때 유리

• 행렬 연산의 최적화

• 맞춤식 디자인 패턴 구현의 생성 - 디자인 패턴에 대한 구현방법이 여러가지일 때 기반을 설계할 수 있음

Page 25: Chapter7~9 ppt

항목 48 정리• 템플릿 메타프로그래밍은 기존 작업을 런타임에서

컴파일 타임으로 전환하는 효과를 냄 . 따라서 TMP를 쓰면 선행 에러 탐지와 높은 런타임 효율을 가질 수 있음

• TMP 는 정책 선택의 조합에 기반하여 사용자 정의 코드를 생성하는 데 쓸 수 있으며 , 또한 특정 타입에 대해 부적절한 코드가 만들어지는 것을 막는 데도 쓸 수 있음

Page 26: Chapter7~9 ppt

항목 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 를 호출

Page 27: Chapter7~9 ppt

new 처리자 함수 처리시 주의할 점

• 사용할 수 있는 메모리를 더 많이 확보

• 다른 new 처리자를 설치

• new 처리자의 설치를 제거 - null pointer 를 넘김

• 예외를 던짐 - bad_alloc 등의 예외를 던짐

• 복귀하지 않음 - abort 혹은 exit ghcnf

Page 28: Chapter7~9 ppt

항목 49 정리

• set_new_handler 함수를 쓰면 메모리 할당 요청이 만족되지 못했을 때 호출되는 함수를 지정할 수 있음

• 예외불가 new 는 영향력이 제한되어 있음 - 메모리 할당 자체에만 적용되기 때문 . 이후에 호출되는 생성자에는 얼마든지 예외를 던질 수 있음

Page 29: Chapter7~9 ppt

항목 50 :new 및 delete 를 언제 바꿔야 좋은 소리를 들을지 파악

• 컴파일러가 준 operator new 와 operator delete 를 바꾸고싶은 이유 ?

• 잘못된 힙 사용을 탐지하기 위해

• 효율을 향상시키기 위해

• 동적 할당 메모리의 실제 사용에 관한 통계 정보를 수집하기 위해

Page 30: Chapter7~9 ppt

다시한번 필요한 조건 체크• 잘못된 힙 사용을 탐지하기 위해

• 동적 할당 메모리의 실제 사용에 관한 통계 정보를 수집하기 위해할당 및 해제 속력을 높이기 위해

• 기본 메모리 관리자의 공간 오버헤드를 줄이기 위해

• 적당히 타협한 기본 할당자의 바이트 정렬 동작을 보장하기 위해

• 임의의 관계를 맺고 있는 객체들을 한 군데애 나란히 모아 놓기 위해

• 그때그떄 원하는 동작을 수행하도록 하기 위해

Page 31: Chapter7~9 ppt

항목 50 정리

• 개발자가 스스로 사용자 정의 new 및 delete 를 작성하는 데는 여러 가지 나름대로 타당한 이유가 있다 여기에는 성능을 향상시키려는 목적 , 힙 사용시의 에러를 디버깅하려는 목적 , 힙사용 정보를 수집하려는 목적 등이 포함

Page 32: Chapter7~9 ppt

항목 51 : new 및 delete 를 작성할 때 따라야 할 기존의 관례를 잘 알아두자• operator new 의 반환 값 부분은 요청된 메모리를 마련해 줄 수 있으면

그 메모리에 대한 포인터를 반환 , 반환할 수 없으면 bad_alloc 호출

• operator new 멤버 함수는 파생 클래스 쪽으로 상속이 되는 함수

• 배열안에 몇 개 의 객체가 들어갈 지 계싼하는 것 어려움 -> 객체 하나가 얼마나 큰지를 확정할 방법이 없음

• operator new[] 에 넘어가는 size_t 타입의 인자는 객체들을 담기에 딱 맞는 메모리 양보다 더 많게 설정되어 있을 수도 있음

• 가상 소멸자가 없는 기본 클래스로부터 파생된 클래스의 객체를 삭제하려고 할 경우에는 operator delete 로 C++ 가 넘기는 size_t값이 엉터리 일 수 있음

Page 33: Chapter7~9 ppt

항목 51 정리• 관레적으로 operator new 함수는 메모리 할당을

반복해서 시도하는 무한 루프를 가져야 하고 , 메모리 할당 요구를 만족시킬 수 없을 때 new처리자를 호출해야 하며 , 0 바이트에 대한 대책도 있어야 함

• 클래스 전용 버전은 자신이 할당하기로 예정된 크기보다 더 큰 메모리 블록에 대한 요구도 처리

• operator delete 함수는 널 포인터가 들어왔을 떄 아무 일도 하지 않아야 함

Page 34: Chapter7~9 ppt

항목 52 : 위치지정 new 를 작성한다면 위치지정 delete 도 같이 준비하자

• 런타임 시스템이 해주어야 하는 일은 자신이 호출한 operator new 함수와 짝이 되는 버전의 operator delete 함수를 호출

• 런타임 시스템은 호출된 operator new 가 받아들이는 매개변수의 개수 및 타입이 똑같은 버전의 operator delete 를 찾고 찾아냈으면 호출 .

• 즉 짝을 이루는 시그너처

• Widget *pw = new Widget

이 실행이 되면 우선 메모리 할당을 위해 operator new 가 호출되고 , 그 뒤를 이어 Widget 의 기본 생성자 호출

이때 기본 생성자 호출을 하면서 예외가 발생했다고 하면 이 메모리는 해제를 해주어야한다 !-> C++ 런타임 시스템에서 맡아서 함

Page 35: Chapter7~9 ppt

위치지정 주의 할 점

• 매개변수를 추가로 받는 new 를 위치지정 이라고 한다

• operator delete역시 이 위치지정과 똑같은 시그너처를 가진 것이어야한다-> 그렇지 않으면 어떤 delete 를 호출하는지 갈팡질팡 하기 때문이다

• 따라서 위치지정을 하면 그에 맞는 delete 도 작성해야 한다

Page 36: Chapter7~9 ppt

항목 52 정리

• operator new 함수의 위치지정 버전을 만들 떄는 이 함수와 짝을 이루는 위치지정 버전의 operator delete 함수도 꼭 만들어야 함 -> 메모리누출 위험성

• new 및 delete 의 위치지정 버전을 선언할 때는 , 의도한 바도 아닌데 이들의 표준 버전이 가려지는 일이 생기지 않도록 주의

Page 37: Chapter7~9 ppt

항목 53: 컴파일러 경고를 지나치지 말자

• 컴파일러 경고를 쉽게 지나치지 말아야 한다 -> 후에 분명히 문제가 생기는 일이 십중팔구

• 컴파일러 경고에 너무 의지하는 일을 하지 말 것 -> 컴파일러마다 경고를 내는 부분이 다르기 때문이다 .

Page 38: Chapter7~9 ppt

항목 54: TR1 을 포함한 표준 라이브러리 구성요소와 편안한 친구가 되자

• 표준 템플릿 라이브러리 (STL) : 컨테이너 , 반복자 , 알고리즘 , 함수객체 등을 담고 있음

• iostream, 국제화 지원 , 수치 처리 지원

• 예외 클래스 계통 지원

• 이러한 라이브러리 구성요소를 사용하여 코딩의 효율성을 키우자

Page 39: Chapter7~9 ppt

항목 55: 부스트를 늘 여러분 가까이에

• 부스트는 C++ 개발자들의 단체이자 무료 다운로드가 가능한 C++ 라이브러리 집합을 동시에 일컫는 고유명사 -> 웹사이트 http://boost.org 에 방문하여 만날 수 있음

• 부스트는 C++ 표준화 위원회와 밀접하고 영향력있는 관꼐 유지

• 라이브러리 승인 심사는 공개 동료 심사에 기반을 두고 있음

• 부스트 라이브러리 군단은 엄청나게 유용하게 쓰임

• 문자열 및 텍스트처리 , 컨테이너 , 함수 객체 및 고차 프로그래밍 , 일반화 프로그래밍 , 템플릿 메타 프로그래밍 , 수학 및 수치조작 , 정확성 유지 및 테스트 , 자료구조 , 타 언어와의 연동 지원 , 메모리 등등