32
Effective C++ Chapter 7, 8 그리고 좀 그런 9 ‘템플릿과 일반화 프로그래밍’, ‘new delet를 내맘대로’ 그리고 ‘그 밖에 이야기’ 131039 신동찬

Effective c++ chapter7_8_9_dcshin

Embed Size (px)

DESCRIPTION

effective c++ 7_8_9 chapter

Citation preview

Page 1: Effective c++ chapter7_8_9_dcshin

Effective C++ Chapter 7, 8 그리고 좀 그런 9‘템플릿과 일반화 프로그래밍’, ‘new delet를 내맘대로’ 그리고 ‘그 밖에 이야기’

131039 신동찬

Page 2: Effective c++ chapter7_8_9_dcshin

어떤 발명은 처음 목표와 달리 위대해 지는 경우가 있다

여학생 외모 비교 사이트 -> 페이스북?!-영화 소셜네트워크에서 그린 페이스북-left? right?

타입에 관계없는 컨테이너를 만들어 타입 안정성 확보가 목표였는데...

새로운 패러다임을 만들었다

템플릿 메타프로그래밍

Page 3: Effective c++ chapter7_8_9_dcshin

템플릿 메타프로그래밍

컴파일러 내부에서 실행되고 컴파일 과정이 끝날 때 실행을 멈추는또 하나의 프로그램

그리고 이것을 활용한 프로그래밍 기법

새롭다고 계속 강조하는데...

그래서 기존의 객체지향 프로그래밍의 세계와 전혀 다르다

• 명시적 인터페이스• 런타임 다형성

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

Page 4: Effective c++ chapter7_8_9_dcshin

암시적 인터페이스

우리가 일반적으로 사용하던 형태는코드에서 class를 작성하고 class 내에서 인터페이스 명시

public으로 인터페이스를 외부에 공개

템플릿이 지정된 scope에 작성된 함수, 연산을 보고필요한 인터페이스를 유추하는 형식

템플릿이 제대로 컴파일 되려면 표현식의 ‘유효성’을 따짐

Page 5: Effective c++ chapter7_8_9_dcshin

컴파일 타임 다형성

템플릿에서 함수가 실행될 때 당연히 인스턴스가 필요(템플릿의 인스턴스화)

어떤 매개변수가 오는지에 따라 다르게 구동하기 때문에당연히 매개변수에 대한 확인이 가능한

컴파일 도중 인스턴스화가 일어난다!

컴파일 도중 동적으로 실행

함수 호출에는 실제 대상인 인스턴스가 필요한데...템플릿은 인스턴스가 어디에 있지?

다형성은 가상 함수의 동적 바인딩을 이야기 하던 것 아닌가?

Page 6: Effective c++ chapter7_8_9_dcshin

템플릿 메타프로그래밍을 시작하는 템플릿어떻게 선언해야 할까?

template<class T> class Widget;

template<typename T> class Widget;

고를 것이 아니라 이 둘은 완전히 같다개인의 취향에 따라 선택

문제는선언에서 사용되는 typename과템플릿 함수 내에서 사용되는 typename이 전혀 다르다는 것

• 의존 이름(중첩되어 사용되면 ‘중첩 의존 이름’)• 비의존 이름

Page 7: Effective c++ chapter7_8_9_dcshin

의존 이름은 템플릿 함수 내의 이름 중 매개변수에 종속 된 것== 템플릿 매개변수에 뭐가 오는가에 따라 변경되는 것

정체가 그때 그때 달라요

컴퓨터는 선언 되지 않은 것에 대해

자기 마음대로 판단할 수 없다

정확하지 않으면 항상 undefined 아니었나...

Page 8: Effective c++ chapter7_8_9_dcshin

template<typename C>void print2nd(const C& container){

if(container.size() >= 2){C::const_iterator iter(container.begin());

}

이 경우 컴파일러가 C가 뭐가 될지 파악할 재간이 없다

template<typename C>void print2nd(const C& container){

if(container.size() >= 2){

typename C::const_iterator iter(container.begin());}

컴파일러가 typename을 보는 순간 위를 다시 한 번 본다‘아! 바로 앞 요녀석이 여기로 온 것이구나!’

Page 9: Effective c++ chapter7_8_9_dcshin

여기서 또 다른 포인트!

template<typename C>void print2nd(const C& container){

if(container.size() >= 2){

typename C::const_iterator iter(container.begin());}

typename 키워드는 중첩 의존 이름만 식별하는데 써야 함그 외 이름은 typename을 가져선 안됨

그런데 여기에 또 예외가 있다!아니 어쩌라고

Page 10: Effective c++ chapter7_8_9_dcshin

template<typename T>class Derived: public Base<T>::Nestedpublic:

explicit Derived(int x):Base<T>::Nested(x){

typename Base<T>::Nested temp;...

예외1. 중첩 의존 타입 이름이 기본 클래스의 리스트에 있는 경우예외2. 중첩 의존 타입 이름이 멤버 초기화 리스트 내의 기본 클래스 식별자로 있는 경우

이 예외를 제외하고는!

복잡성, 왠지 안될 것 같은 조합 등등상관없이 typename을 붙인다그래도 컴파일러마다 또 다르다는 점은 함정

Page 11: Effective c++ chapter7_8_9_dcshin

멍청한 컴파일러!!

지금까지 똑똑한 척 다하던 컴파일러가또 발견 못하는 것이 있으니...

기본 클래스(부모)에 선언된 함수를 못 찾는 문제 발생

왜 발생하는가?템플릿 매개 변수에 따라 어떤 인스턴스가 될지 알 수 없음기본 클래스 인스턴스에 함수가 들어 있다면

컴파일러가 어떤 함수를 불러야 될지 알 수 없는 것이 당연

Page 12: Effective c++ chapter7_8_9_dcshin

일단 컴파일러는 나중에 처리하고

거기에 모두가 같은 템플릿을 쓰지만혼자만 다른 동작을 취하고 싶은 클래스가 있다면?!

class CompanyA{public:…void sendCleartext(const std::string& msg);void sendEncryped(const std::string& msg);…};

class MsgInfo{ … };

template<typename Company>class MsgSender{public:

void sendClear)const MsgInfo& info){

std::stirng msg;

Company c;c.sendCleartext(msg);

...

class CompanyZ{public:

void sendEncrypted(const std::string& msg);...

};

template<>class MsgSender<CompanyZ>{public:

void sendSecret(const MsgInfo& info){ ... }

};

완전 템플릿 특수화를 이용해 이렇게 추가~!

Page 13: Effective c++ chapter7_8_9_dcshin

컴파일러의 멍청함 + 말이 안 되는 함수!!

Company == CompanyZ가 되는 순간CompanyZ에 없는 함수가 멤버가 되어 버릴수도 있는 사태가!

컴파일러가 템플릿으로 만들어진 기본 클래스를 뒤져상속된 이름을 찾는 것을 거부하는 것

Page 14: Effective c++ chapter7_8_9_dcshin

둘 다 해결해야 프로그램이 돈다!

해결1. 기본 클래스 함수에 대한 호출문 앞에 this->를 사용해결2. using 선언으로 기본 클래스에 함수가 있다고 알림해결3. 호출할 함수가 기본클래스 함수라고 명시(비추)

멍청한 컴파일러에게 함수 위치 안내-> 컴파일 에러 방지

템플릿화된 기본 클래스를 멋대로 뒤지지 않게 함-> 특수화 버전 사용 가능

Page 15: Effective c++ chapter7_8_9_dcshin

템플릿 메타프로그래밍이점점 좋아지는데?

이제 계산기를 두드릴 때가 되었습니다.항상 좋기만 한 것은 컴퓨터 세상에 없어요

Trade Off

비슷비슷한 클래스 작성 치우고템플릿 하나 쓰면컴파일러가 알아서?

알아서 하는 대신 코드 비대화가 발생합니다code 영역도 무료일리가 없잖아...

Page 16: Effective c++ chapter7_8_9_dcshin

해결 step1. 공통성 및 가변성 분석

말이 어렵지 내용은 우리가 잘 아는 것

유사한 내용을 한데 묶고다른 부분은 각각 유지하도록 하는 것

template<typename T, std::size_t n>class SquareMatrix{public:

…void invert();

};

SquareMatrix<double, 5> sm1;…sm1.invert();

SquareMatrix<double, 10>sm2;…sm2.invert();

template<typename T>class SquareMatrixBase {protected:

void invert(std::size_t matrixsize);};

template<typename T, std::size_t n>class SquareMatrix{private:

using SquareMatrixBase<T>::invert;

public:…void invert(){ this -> invert(n) };

}; 같은 타입의 행렬은한 SqareMatrixBase를 공유

코드 비대화가 줄어듦

Page 17: Effective c++ chapter7_8_9_dcshin

해결 step2. 진짜 행렬을 저장한 데이터 전달을 위해 포인터 전달

진짜가 나타나게 해줘야 한다!

template<typename T>class SquareMatrixBase {protected:

void invert(std::size_t matrixsize);};

template<typename T, std::size_t n>class SquareMatrix{private:

using SquareMatrixBase<T>::invert;

public:…void invert(){ this -> invert(n) };

};

다 좋은데...객체가 더 커져서 code에 두고 싫어지면?

생성자에서 데이터를 입에 둘 수도 있음

template<typename T>class SquareMatrixBase {protected:

void invert(std::size_t n, T *pMem):size(n), pData(pMem) { }

void setDataPtr(T *ptr) { pData = ptr; }

private:std::size_t size;T *pData;

};

template<typename T, std::size_t n>class SquareMatrix{public:

SquareMatrix():SquareMatrixBase<T>(n, data) {}

private:T data[n*n];

};

Page 18: Effective c++ chapter7_8_9_dcshin

원하는 대로 했으나...완전히 비용을 줄이는 방법은 아님

비대화를 약간 감소시킨 정도

게다가 코드 좀 줄이려다가엄청 복잡한 구조를 만들어 낸 느낌

Trade-off 잘 보고

적절한 수준으로 사용하는 것이 미덕!

Page 19: Effective c++ chapter7_8_9_dcshin

같은 템플릿 출신이면 인스턴스들 사이에 관계는 어떨까?같은 붕어빵틀에서 찍어내면 똑같다고 생각하기 쉬운데

충격적이게도

어떤 관계도 없다

따라서 템플릿을 통해 생성된 인스턴스끼리어떤 변환을 하고 싶다면변환이 되도록 직접 프로그램을 만들어야 함

그리고 이건 멤버 함수 템플릿으로 처리하자

Page 20: Effective c++ chapter7_8_9_dcshin

template<Class T> class shared_ptr{public:

template<Class Y>explicit shared_ptr(Y * p);

template<Class Y>shared_ptr(shared_ptr<Y> const& r);

template<Class Y>explicit shared_ptr(week_ptr<Y> const& r);

한 땀 한 땀 변경 사항을 함수로 만들면원하는 기능을 획득할 수 있습니다.

주의!!일반화 복사 생성 연산과 일반화 대입 연산을 위해 멤버 템플릿을 선언해도해결이 안 되는 것이 있으니...

보통의 복사 생성자와 복사 대입 연산자는 여전히 직접 선언해야!

Page 21: Effective c++ chapter7_8_9_dcshin

예전에 본 나쁜 ‘일진 친구’ friend가 갱생했다!

타입 변환이 바람직한 경우에는friend를 클래스 템플릿 내부에 정의하는 것이 좋다!

클래스 안에 정의된 함수는 암시적으로 인라인으로 선언프렌드 함수도 예외가 아님

따라서 클래스 바깥에서 정의된 도우미 함수만 호출해암시적 인라인 선언의 영향을 최소화 할 수 있음

Page 22: Effective c++ chapter7_8_9_dcshin

컴파일러는 여전히 배고프다

지금까지 어떻게 하면 컴파일러에게 정보를 줄 수 있을까 고민했다.하지만 컴파일 상황에서 프로그램을 구동하려면 더 필요하다

그렇다면 사용자가 정보를 챙겨줄 수는 없을까?

특성 정보 클래스가 답!

Page 23: Effective c++ chapter7_8_9_dcshin

특성 정보 클래스는컴파일 도중에 사용할 수 있는 타입 관련 정보를 만들어 낸다

• 타입 관련 정보를 확인할 수 있게 하고• 해당 정보를 가진 템플릿을 지원• 특수화 버전 템플릿도 지원

template<typename IterT, typename DistT>void doAdvance(IterT& iter, DistT d, std::random_access_iterator_tag){

iter += d;}

template<typename IterT, typename DistT>void doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag){

if ( d >= 0 ) {while (d--) ++iter;}else { while (d++) –iter; }

}

template<typename IterT, typename DistT>void doAdvance(IterT& iter, DistT d, std::input_iterator_tag){…

iter에 여러 종류가 있다고 하던데...해당 내용을 써주면 된다

각 옵션에 따라 실행이 달라진다

Page 24: Effective c++ chapter7_8_9_dcshin

이렇게 복잡한데?템플릿 메타프로그래밍이 필요해?

template<unsigned n>struct Factorial {

enum { value = n* Factorial<n-1>::value };};

template<>struct Factorial<0> {

enum { value = 1 };};

흔한 템플릿 메타 프로그래밍 factorial

뿐만 아니라 이 작업이 컴파일 타임에서!

성능을 원치 않았다면?C++가 필요해?와 뭐가 다른가...다른 좋은 언어 많다

Page 25: Effective c++ chapter7_8_9_dcshin

말 나온 김에 성능을 더 뽑아내 봅시다

우리가 아무 생각 없이 사용하는 함수들과연 최선일까요?

특히나 메모리 자원을 수동으로 관리하기에C++을 선택하신 분이 많잖아요?

매번 사용하는

New, Delete함수는 과연?

Page 26: Effective c++ chapter7_8_9_dcshin

New에도 함정이 있었으니...

함정1. New를 했는데 정말로 메모리가 없다면?함정2. 메모리는 할당 잘 했는데 오류가 나는 경우?(포인터 잃어버림ㅠㅠ)

이런 경우를 처리하는 다양한 방법이 있어요

• 다른 new 처리자(set_new_handler)를 설치• new 처리자의 설치를 제거• 예외를 던짐• 복귀하지 않고 프로그램 종료

이런 기능을 추가한 new를 직접 만들자!

Page 27: Effective c++ chapter7_8_9_dcshin

그런데...이미 있는 함수 바꾸면 다른 객체에도 영향을 줄 수 있기에처리할 내용이 많습니다.ex) 자식 클래스의 new는 원래꺼 쓰고 싶은데...

이미 부모 클래스의 new가 이름을 덮어 버렸어 등

new를 바꿔서 칭찬 받는 경우

• 잘못된 힙 사용 탐지• 동적 할당 메로리 사용 통계 수집• 할당 및 해제 속도 상승• 기본 메모리 관리자 공간 오버헤드 감소• 바이트 정렬 동작 보장(alignment)• 임의의 관계를 맺는 객체를 한 군데 모으기 위해(locality)• 원하는 동작을 수행하도록 하기 위해

Page 28: Effective c++ chapter7_8_9_dcshin

Delete는 왜 아무 말이 없어?

말이 없는 것이 아니라...New와 Delete는 쌍이라는 것을 잊어서는 안 된다!New가 바뀌면 Delete도 New에 따라 가야 됨!

그것도 관례에 맞춰

• new 함수는 메모리 할당을 1번 시도하는 게 아니다반복 시도하는 무한 루프가 있어야 함

• 할당 실패에 대한 처리가 필요

• delete는 널포인터가 들어오면 아무일도 하지 않음

Page 29: Effective c++ chapter7_8_9_dcshin

이제부터 그 밖의 이야기들을 하고자 합니다

• 컴파일러 경고를 지나치지 말 것• TR1을 포함한 표준 라이브러리 구성요소와 친해지자• 부스트를 늘 가까이에

컴파일러 경고만 아직 피부에 와 닿고

나머지는 문서에 대한 이야기문서를 실체화 하려는 노력에 대한 이야기입니다.

TR1이 문서였어요...shared_ptr있길래 기능인가 했는데

Page 30: Effective c++ chapter7_8_9_dcshin

Class B {public:

virtual void f() const;};

Class D : public B {public:

virtual void f();};

컴파일 하면warning: d::f() hides virtual b::f()

왜 이러는 걸까요?class B의 f()가 아예 없는 존재가 되어 버렸기 때문!

우리는 D에서 f()를 재선언했다고 생각하기 쉽다하지만 아예 가려저 버린 상태

괜히 warning 나오는 것이 아니다.은자림팀에는 warning 포비아 환자가 있다라던가…카더라

Page 31: Effective c++ chapter7_8_9_dcshin

TR1• 표준 템플릿 라이브러리• iosteam• 국제화 지원...

Boost• 문자열 및 텍스트 처리• 컨테이너• 일반화 프로그래밍• 수학 및 수치 조작...

명세서와 해당 기능의 구현계속 찾아보며 코딩합시다

현업에서 ‘선배님! 우리 부스트 쓰죠?’ 하면어떻게 되나요??

Page 32: Effective c++ chapter7_8_9_dcshin

effective C++ 끝