effective c++ chapter 7,8

67
Effective C++ Chapter 7,8 NHN NEXT 장문익

Upload: -

Post on 21-Jul-2015

98 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: Effective c++ chapter 7,8

Effective C++ Chapter 7,8NHN NEXT 장문익

Page 2: Effective c++ chapter 7,8

Effective C++ Chapter 7

Page 3: Effective c++ chapter 7,8

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

• 객체 지향 프로그래밍은 명시적 인터페이스(explicit interface)와 런타임 다형성(runtime polymorphism)

• explicit interface: 소소 코드에서 찾으면 어떤 형태인지 확인할수 있는 인터페이스

• runtime polymorphism: 가상 함수에 대한 실제 호출은 동적 타입을 기반으로 프로그램 실행 중, 즉 런타임에 결정된다.

Page 4: Effective c++ chapter 7,8

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

• implicit interface: 템플릿 안의 매개변수가 지원해야 하는 인터페이스는 w에 대해 실행되는 함수가 결정한다.

• compile time polymorphism: 매개변수가 호출될 때는 해당 호출을 성공시키기 위해 템플릿 인스턴스화가 이루어진다. 인스턴스화는 컴파일 도중이다. 인스턴스화를 진행하는 함수 템플릿에어떤 템플릿 매개변수가 들어가느냐에 따라 호출되는 달라지기때문에 컴파일 타임 다형성이라 한다.

Page 5: Effective c++ chapter 7,8

함수 시그너처

• 함수 시그너처: 함수의 이름, 매개변수 타입, 반환 타입 등을 통들어 부르는 용어

• 암시적 인터페이스는 함수 시그너처에 기반하고 있지 않다.

Page 6: Effective c++ chapter 7,8

암시적 인터페이스와 표현식에 대한 제약

Page 7: Effective c++ chapter 7,8

표현식에 대한 제약

if 문의 조건식 부분은 boolean이어야 한다.

이와 같은 제약이 바로 doProcessing 템플릿이 타입 매개변수인T에 대해 요구하는 암시적 인터페이스의 일부이다.

복사 생성자, normalize, swap 함수에 대한 호출이 T 타입의 객체에 대해 유효해야 한다.

Page 8: Effective c++ chapter 7,8

항목 42: typename의 두 가지 의미

• 위의 두 템플릿 선언문에 쓰인 class와 typename의 차이점?

• 템플릿의 타입 매개변수를 선언할 때는 class와 typename의 뜻이 완전히 똑같다.

Page 9: Effective c++ chapter 7,8

의존 이름과 비의존 이름

Page 10: Effective c++ chapter 7,8

의존이름(dependent name)

• 템플릿 내의 이름 중에 템플릿 매개변수에 종속된 것을 의존이름이라 한다.

• C::const_iterator가 중첩 이름

Page 11: Effective c++ chapter 7,8

비의존 이름(non-dependent name)

• int는 템플릿 매개변수가 어떻든 상관없는 타입이다.

• 이런 이름은 비의존 이름이다.

Page 12: Effective c++ chapter 7,8

중첩 의존 이름 사용시에는 typename

• iter가 선언이 되기 위해서는 C::const_iterator가 타입이어야 한다.

• 컴파일러에게 C::const_iterator가 타입이라는 것을 알려주지 않는 이상 컴파일러는 알 수 없다.

• 그래서 typename 키워드를 사용해야 한다.

Page 13: Effective c++ chapter 7,8

중첩 의존 이름과 typename 사용의 예외

Page 14: Effective c++ chapter 7,8

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

Page 15: Effective c++ chapter 7,8

항목44: 매개변수에 독립적인 코드는 템플릿으로

Page 16: Effective c++ chapter 7,8

코드 비대화 개선

Page 17: Effective c++ chapter 7,8

행렬값을 담는 메모리 포인터 추가

Page 18: Effective c++ chapter 7,8

위 설계의 장점

• 위의 설계로 멤버 함수 중 상당수가 기본 클래스 버전을 호출하는 단순 인라인 함수가 될 수 있었다.

• 똑같은 타입의 데이터를 원소로 갖는 모든 정방행렬들이 행렬크기에 상관없이 기본 클래스 버전의 사본 하나를 공유한다.

• 실행코드가 작아져서 프로그램의 작업 세트 크기가 줄어들기때문에 명령어 캐시 내의 참조 지역성도 향상된다.

• 이는 프로그램 실행 속도와도 큰 관련이 있다.

Page 19: Effective c++ chapter 7,8

정리

• 템플릿을 사용하면 비슷한 클래스와 함수가 여러 개 만들어 진다.

• 매개변수에 종속되지 않은 템플릿 코드는 비대화의 원인

• 비타입 템플릿 매개변수로 생기는 코드 비대화는 템플릿 매개변수를 함수 매개변수 혹은 클래스 데이터 멤버로 대체한다.

• 타입 매개변수로 생기는 비대화는 동일한 이진 표현구조를 가지고 인스턴스화되는 타입들이 한 가지 함수 구현을 공유하게만든다.

Page 20: Effective c++ chapter 7,8

항목45: 멤버 함수 템플릿

Page 21: Effective c++ chapter 7,8

일반화 복사 생성자

Page 22: Effective c++ chapter 7,8

일반화 복사 생성자 사용

Page 23: Effective c++ chapter 7,8

정리

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

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

Page 24: Effective c++ chapter 7,8

항목46: 타입변환할 경우, 비멤버 함수를클래스 템플릿 안에 정의

Page 25: Effective c++ chapter 7,8

링크 문제 해결

Page 26: Effective c++ chapter 7,8

프렌드 함수는 도우미만 호출하게 만들기

Page 27: Effective c++ chapter 7,8

프렌드 함수 도우미의 헤더 파일 내의 모습

Page 28: Effective c++ chapter 7,8

정리

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

Page 29: Effective c++ chapter 7,8

항목47: 타입에 대한 정보는 특성정보 클래스로

Page 30: Effective c++ chapter 7,8

STL Iterator – input iterator

• 입력 반복자(input iterator)

• 전진만 가능하고, 한 번에 한 칸씩만 이동하며, 자신이 가리키는위치에서 읽기만 가능하다.

• 읽을 수 있는 횟수도 한 번이다.

• 입력 파일에 대한 읽기 전용 파일 포인터를 본떠서 만들었다. (예 istream_iterator)

Page 31: Effective c++ chapter 7,8

STL Iterator – output iterator

• 출력 반복자(output iterator)

• 입력 반복자와 비슷하지만 출력용인 점만 다르다.

• 즉, 오직 앞으로만 가고, 한 번에 한 칸씩만 이동하며, 자신이 가리키는 위치에서 쓰기만 가능하다.

• 쓸 수 있는 횟수가 딱 한 번이다.

• 이 출력 반복자는 출력 파일에 대한 쓰기 전용 파일 포인터를본떠서 만들었다.(예 ostream_iterator)

Page 32: Effective c++ chapter 7,8

STL Iterator – forward iterator

• 순방향 반복자(forward iterator)

• 입력 반복자와 출력 반복자가 하는 일은 기본적으로 다 할 수있다.

• 자신이 가리키고 있는 위치에서 읽기와 쓰기를 동시에 할 수 있다. 여러 번 가능하다.

• 이 반복자는 multi-pass 알고리즘에 사용할 수 있다.

Page 33: Effective c++ chapter 7,8

STL Iterator – bidirectional iterator

• 양방향 반복자(bidirectional iterator)

• 순방향 반복자에 뒤로 갈 수 있는 기능을 추가한 것이다.

• STL list에 쓰는 반복자가 이 범주에 속한다.

Page 34: Effective c++ chapter 7,8

STL Iterator – random access iterator

• 임의 접근 반복자(random access iterator)

• 양방향 반복자에 “반복자 산술 연산(iterator arithmetic” 수행 기능을 추가하였다.

• 반복자를 임의의 거리만큼 앞뒤로 이동시키는 일을 상수 시간안에 할 수 있다.

Page 35: Effective c++ chapter 7,8

STL iterator tag struct 간의 관계

Page 36: Effective c++ chapter 7,8

• 각각 반복자들의 가능한 것, 불가능 한 것이 분명하다.

• 최소 공통 분모 전략으로 input_iterator의 기능으로 통일한다면선형 시간이 걸린다.

• 상수 시간의 반복자 산술 연산을 쓸 수 있는 임의 접근 반복자의 좋은 기능을 버리게 된다.

Page 37: Effective c++ chapter 7,8

타입의 특성 정보가 필요한 경우

Page 38: Effective c++ chapter 7,8

특성정보(traits)

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

Page 39: Effective c++ chapter 7,8

특성정보 구조체

Page 40: Effective c++ chapter 7,8

부분 템플릿특수화(partial template specialization)

Page 41: Effective c++ chapter 7,8

타입 정보 파악 시점

Page 42: Effective c++ chapter 7,8

if문을 오버로드 함수형태로

Page 43: Effective c++ chapter 7,8

특성 정보 클래스 사용 정리

• “작업자(worker)” 역할을 맡을 함수 혹은 함수 템플릿을 특성정보 매개변수를 다르게 하여 오버로딩한다.

• 전달되는 해당 특성정보에 맞추어 각 오버로드 버전을 구현한다.

• 작업자를 호출하는 “주작업자(master)” 역할을 맡을 함수 혹은함수 템플릿을 만든다.

• 이때 특성정보 클래스에서 제공되는 정보를 넘겨서 작업자를호출하도록 구현한다.

Page 44: Effective c++ chapter 7,8

항목48: 템플릿 메타프로그래밍

• 템플릿 테마프로그래밍

• template metaprogramming(TMP)

• 컴파일러가 실행시키는, C++로 만들어진 프로그램

• TMP 프로그램이 실행을 마친 후엔 그 결과로 나온 출력물이 다시 보통의 컴파일 과정을 거치는 것

• TMP를 쓰면 다른 방법으로는 까다롭거나 불가능한 일을 굉장히 쉽게 할 수 있다.

• C++ 컴파일이 진행되는 동안에 실행되기 때문에, 기존 작업을런타임 영역에서 컴파일 타임 영역으로 전환할 수 있다.

Page 45: Effective c++ chapter 7,8

TMP 예시

Page 46: Effective c++ chapter 7,8

TMP를 어디에 쓸까?

• 치수 단위(dimensional unit)의 정확성 확인: 선행 에러 탐지

• 행렬 연산의 최적화: 표현식 템플릿을 사용하면 덩치 큰 임시 객체를 없애는 것은 물론이고, 루프까지 합쳐 버릴 수 있다.

• 맞춤식 디잔인 패턴 구현의 생성: 정책 기반 설계(policy-based design), 생성식 프로그램(generative programming)

Page 47: Effective c++ chapter 7,8

Effective C++ Chapter 8

Page 48: Effective c++ chapter 7,8

항목49: new 처리자의 동작원리

• new 처리자(new-handler, 할당에러 처리자)

• 메모리 할당이 제대로 되지 못한 상황에 대한 반응으로operator new가 예외를 던지기 전에, 이 함수는 사용자 쪽에서지정할 수 있는 에러 처리 함수를 우선적으로 호출하도록 되어있다.

• 이 에러 처리 함수를 가리켜 new 처리자라고 한다.

Page 49: Effective c++ chapter 7,8

할당에러 처리자를 두는 방법

• 해당 클래스에서 자체 버전의 set_new_handler 및 operator new를 제공하도록 만들어주면 된다.

• set_new_handler 함수의 역할은 그 클래스에 쓰기 위한 new 처리자를 받아내는 것이다.

• operator new 함수는 그 클래스 객체를 담을 메모리가 할당되려고 할 대 전역 new 처리자 대신 클래스 버전의 new 처리자가호출되도록 만드는 역할을 맡는다.

Page 50: Effective c++ chapter 7,8

set_new_handler 함수

Page 51: Effective c++ chapter 7,8

operator new의 역할

• 표준 set_new_handler 함수에 Widget의 new 처리자를 넘겨서호출한다. 즉, 전역 new 처리자로서 Widget의 new 처리자를 설치한다.

• 전역 operator new를 호출하여 실제 메모리 할당을 수행한다. 전역 operator new의 할당이 실패하면, 이 함수는 Widget의new 처리자를 호출한다.

• 전역 operator new가 메모리 할당 실패하면 bad_alloc 예외를던진다. operator new는 전역 new 처리자를 원래 것을 되돌려야 한다.

• 전역 operator new가 Widget 객체 하나만큼의 메모리를 할당할 수 있으면, Widget의 operator new는 메모리를 반환한다.

Page 52: Effective c++ chapter 7,8

nothrow new

Page 53: Effective c++ chapter 7,8

정리

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

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

Page 54: Effective c++ chapter 7,8

항목50: new, delete 바꾸기

• operator new와 operator delete 바꿔야 할 필요가 있는 경우

• 잘못된 힙 사용을 탐지하기 위해: 데이터 오버런 및 언더런이 발생할 수 있는 경우 대비하여 탐지용 바이트 패턴(signature)을추가한다.

• 효율을 향상시키기 위해

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

Page 55: Effective c++ chapter 7,8

operator new 바꾸기

Page 56: Effective c++ chapter 7,8

바이트 정렬과 operator new

• 모든 operator new 함수는 어떤 데이터 타입에도 바이트 정렬을 적절히 만족하는 포인터를 반환해야 한다.

• 앞에서 바꾼 operator new는 malloc에서 나온 포인터를 int 크기만큼 수정한 포인터를 반환하기 때문에 안전하다는 보장을할 수 없다.

Page 57: Effective c++ chapter 7,8

정리:새로운 new와 delete가 필요한 경우

• 잘못된 힙 사용 탐지

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

• 할당 및 해제 속도를 높이기 위해

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

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

• 임의의 관계를 맺고 있는 객체들을 한 군데에 나란히 모으기

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

Page 58: Effective c++ chapter 7,8

항목51: new, delete 사용 관례

Page 59: Effective c++ chapter 7,8

정리

• operator new 함수는 메모리 할당을 반복해서 시도하는 무한루프를 가져야하고, 메모리 할당 요구를 만족시킬 수 없을 때new 처리자를 호출해야 하며, 0바이트에 대한 대책도 있어야한다. 클래스 전용 버전은 자신이 할당하기로 예정된 크기보다더 큰(틀릭) 메모리 블록에 대한 요구도 처리해야 한다.

• operator delete 함수는 널 포인터가 들어왔을 대 아무 일도 하지 않아야 한다. 클래스 전용 버전의 경우에는 예정 크기보다 더큰 블록을 처리해야 한다.

Page 60: Effective c++ chapter 7,8

항목52: 위치지정 new, delete

• Widget *pw = new Widget();

• 위의 new 표현식은 함수 두 개를 호출한다.

• operator new, Widget의 기본 생성자

• 만약 operator new가 호출된 후, Widget의 기본 생성자가 호출이 진행되다 예외가 발생하면 첫 함수에 의해 할당된 메모리는어떻게 취소할 것인가?

• 생성자에서 예외가 튀어나오면 pw에 포인터가 대입될 일도 없는데 어떻게 해제할 것인가?

Page 61: Effective c++ chapter 7,8

위치지정(placement) new

• 개념적으로 그냥 ‘추가 매개변수를 받는 new’이므로 위치지정new는 가지각색이다.

• 그 중 유용한 형태가 생성시킬 메모리 위치를 나타내는 포인터를 매개변수로 받는 것이다.

void* operator new(std::size_t, void *pMemory) throw();

Page 62: Effective c++ chapter 7,8

위치지정 delete

• 매개변수를 추가로 받아들인다는 점에서 위치지정 new와 비슷한다.

void operator delete(void *, std::ostream&) throw();

Page 63: Effective c++ chapter 7,8

위치지정 new와 delete는 짝이 되어야

Page 64: Effective c++ chapter 7,8

전역 유효 범위 operator new의 표준

Page 65: Effective c++ chapter 7,8

표준 new delete를 기능 모음 클래스

Page 66: Effective c++ chapter 7,8

위 클래스를 상속 받아서 만들기

Page 67: Effective c++ chapter 7,8

정리

• operator new 함수의 위치지정 버전을 만들 때는, 이 함수와 짝을 이루는 위치지정 버전의 operator delete 함수도 꼭 만들자.

• 그러지 않으면 찾아내기도 힘들고 생겼다 안 생겼다 하는 메모리 누출을 경험하게 된다.

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