64
EFFECTIVE C++ 정리 Chapter 4 설계 및 선언

Effective c++ 정리 chapter 4

  • Upload
    -

  • View
    131

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Effective c++ 정리 chapter 4

EFFECTIVE C++ 정리Chapter 4

설계 및 선언

Page 2: Effective c++ 정리 chapter 4

잘쓰기는 쉽게못쓰기는 어렵게

ITEM 18

Page 3: Effective c++ 정리 chapter 4

Item 18: 잘쓰기 쉽게 못쓰기 어렵게

• 인터페이스

• 유저와 코드가 접하는 경로

• 잘쓰기는 쉽게 못쓰기는 어렵게 만들자

• 유저가 생각하는 대로 동작하게 하자.

• 잘못 쓴 경우 뭔가 항의의 몸부림을 치자.

• 사용자가 실수하는 경우를 머리에 두고 있자.

Page 4: Effective c++ 정리 chapter 4

Item 18: 잘쓰기 쉽게 못쓰기 어렵게

• 날짜 클래스의 생성자

Date( int month, int day, int year);

• 매개변수 순서를 잘못 입력한 경우

Data d(30, 3, 1995); //3, 30이 올바른 사용

• 가능한 범위를 넘어가는 경우

Data d(3, 40, 1995); //3월 40일은 없다.

Page 5: Effective c++ 정리 chapter 4

Item 18: 잘쓰기 쉽게 못쓰기 어렵게

• Day, Month, Year 각각 wrapper 타입을 만들자.

• struct Day {explicit Day(int day)…} //Month, Year 이하 동일

• Data (const Month& m, const Day& d, const Year& y);

• Date d(30, 3, 1995); //타입 체크 오류

• Data d(Day(30), Month(3), Year(1995)); //타입 체크 오류

• Data d(Month(3), Day(30), Year(1995); //올바른 사용

Page 6: Effective c++ 정리 chapter 4

Item 18: 잘쓰기 쉽게 못쓰기 어렵게

• 타입의 값에 제약을 가해보자.

• 유효한 Month는 12개 뿐

• Enum ? 타입 안전성이 불안해 (http://ozt88.tistory.com/15 참고)

• static 함수로 Month를 반환하는 방식을 사용하자. (item 4참고)

public:

static Month Jan(){return Month(1); … //Month를 만드는 유일한 창구

private:

explicit Month(int m); //생성자를 private해서 안전하게

Page 7: Effective c++ 정리 chapter 4

Item 18: 잘쓰기 쉽게 못쓰기 어렵게

• 또 다른 제약 붙이기 const (item 3 참조)

• operator*의 반환 타입을 const로 한정한 이유

• if( a * b = c )… //이런 실수를 막기 위해서

• 별다른 이유가 없다면 클래스는 기본 타입처럼 동작하게 하자

• 기존 int 역시 *연산에 다른 값을 대입할 수 없게 만들어져있음.

• 유사한 쓰임새의 기본타입의 동작을 모방하자

• 일관성 있는 인터페이스의 제공

Page 8: Effective c++ 정리 chapter 4

Item 18: 잘쓰기 쉽게 못쓰기 어렵게

• STL에서 나타나는 비일관성의 예

• 데이터의 길이 또는 크기를 나타낼 떄

• Array는 length 프로퍼티로

• String은 length 메서드로

• List는 size 메서드로

• 비 일관성은 인터페이스를 해친다.

Page 9: Effective c++ 정리 chapter 4

Item 18: 잘쓰기 쉽게 못쓰기 어렵게

• 사용자 쪽에서 뭔가 외워야 하는 인터페이스는 잘못 되었다.

• Investment* createInvestment(); //팩토리 함수 (in item 13)

• 사용자 측에서 delete하는 것을 잊어서는 안 된다.

• 사용자 측에서 별 고민 없이 그냥 사용할 수 있게

• std::shared_ptr<Investment> createInvestment(); //스마트포인터 반환

• 자동 해제되어 편하게 사용가능

• 사용자 실수를 원천 봉쇄 ( deleter도 설계자가 지정 가능 )

Page 10: Effective c++ 정리 chapter 4

Item 18: 잘쓰기 쉽게 못쓰기 어렵게

• 사용자 쪽에서 뭔가 외워야 하는 인터페이스는 잘못 되었다.

• Investment* createInvestment(); //팩토리 함수 (in item 13)

• 사용자 측에서 delete하는 것을 잊어서는 안 된다.

• 사용자 측에서 별 고민 없이 그냥 사용할 수 있게

• std::shared_ptr<Investment> createInvestment(); //스마트포인터 반환

• 자동 해제되어 편하게 사용가능

• 사용자 실수를 원천 봉쇄 ( deleter도 설계자가 지정 가능 )

Page 11: Effective c++ 정리 chapter 4

Item 18: 잘쓰기 쉽게 못쓰기 어렵게

• 교차 DLL 문제

• 자원 할당된 포인터가 여기저기 돌아다니는 경우

• 객체 생성시에 특정 DLL에 있는 new를 사용

• 해제 시에는 다른 DLL에 있는 delete 를 사용

• tr1::std::shared_ptr은 그런 걱정 ㄴㄴ

• Shared_ptr은 자신의 deleter를 control block에서 관리하기 때문

• 훌륭한 인터페이스의 대표적 사례 (http://ozt88.tistory.com/28 참조)

Page 12: Effective c++ 정리 chapter 4

클래스 설계는타입을 설계하듯

ITEM 19

Page 13: Effective c++ 정리 chapter 4

Item 19: 클래스 설계는 타입을 설계하듯

• 클래스를 설계할 때, 새로운 언어의 타입을 설계하듯

• 자연스러운 구문, 의미체계의 구축

• 직관적이며 동시에 효율적인 기능 구현

• 고려사항들 1

• 객체 생성 소멸 방식

• 객체 초기화와 대입연산의 차이

• 값 복사의 의미와 구문

Page 14: Effective c++ 정리 chapter 4

Item 19: 클래스 설계는 타입을 설계하듯

• 고려사항들 2

• 타입의 domain 설정

• 값 제약

• Setter들에서 조건 체크

• 멤버 상속여부

• 상속 받았다면 어떤 멤버의 속성을 상속받아 사용할 것인가?

• 상속해 줄 것이라면 어떤 멤버를 가상으로 설정할 것인가?

Page 15: Effective c++ 정리 chapter 4

Item 19: 클래스 설계는 타입을 설계하듯

• 고려사항들 3

• 타입 변환

• 어떤 타입들로 변환을 허락할 것인가

• 암시적 변환/ 명시적 변환

• 적당한 연산자와 멤버 함수 선정

• special function 중 어떤 것을 취사 선택하여 사용할 것인가?

• Private로 봉인 가능

• 멤버함수 접근 권한 설정 (public/private/protected)

Page 16: Effective c++ 정리 chapter 4

Item 19: 클래스 설계는 타입을 설계하듯

• 고려사항들 4

• 선언되지 않은 인터페이스

• 보장할 자원, 성능, 예외 안정성

• 일반적 템플릿을 만들어야 하는가?

• 정말로 필요한 타입인가?

• 비슷한 클래스가 있고 기능이 몇 개 추가되지 않는다면

• 일반 비멤버 함수를 만들거나 템플릿을 몇 개 더 정의하는 게 낫다.

Page 17: Effective c++ 정리 chapter 4

객체 전달은 const T&ITEM 20

Page 18: Effective c++ 정리 chapter 4

Item 20: 객체 전달은 const T&

• C++ 함수의 동작은 기본적으로 값에 의한 전달

• 일반 객체 paramete는 argumen에 대한 사본으로 생성

• 반환 값의 사본을 만들어 반환 객체를 받는다.

• 예제 코드 Person class와 그를 상속받는 Student Class

Page 19: Effective c++ 정리 chapter 4

Item 20: 객체 전달은 const T&

• class Person{

public:

Person();

virtual ~Person();

private:

std::string name;

std::string address;

};

• class Student : public Person{

public:

Student();

virtual ~Student();

private:

std::string schoolName;

std::string schoolAddress;

};

Page 20: Effective c++ 정리 chapter 4

Item 20: 객체 전달은 const T&

• Student를 인자로 전달받는 함수 validateStudent

bool validataStudent(Student s);

Student plato;

bool platoIsOK = validateStudent(plato);

• 객체 전달에 필요한 비용

• Parameter의 Student 복사 생성자 호출

• Argument의 Student 소멸자 호출

Page 21: Effective c++ 정리 chapter 4

Item 20: 객체 전달은 const T&

• 좀더 구체적인 비용

• Student 생성할 때 덩달아 불리는 생성자들

• 멤버변수 SchoolName 생성자, SchoolAddress 생성자 호출

• 상속받은 Person의 생성자 호출

• Person의 멤버변수 address, name 생성자 호출

• Student 해제할때 덩달아 호출되는 소멸자들

• 위에서 생성된 모든 객체들 각각 해제

• TOTAL : 생성자 6번, 소멸자 6번

Page 22: Effective c++ 정리 chapter 4

Item 20: 객체 전달은 const T&

• 비효율적인 값 전달의 대체제 const T&

• Reference-to-Const

• 어차피 값으로 전달하면, 기존 객체의 값이 바뀔 일이 없다는 의미

• 상수성이 유지되면 사본을 만드는 대신 상수 객체의 참조를 넘기는게 이득

• No 생성 No 소멸 No 복사

• bool validateStudent( const Student& s );

• 복사손실 문제를 해결

• 정적 타입이 부모 클래스인 경우, 부모클래스의 복사 생성자 호출로 데이터 손실

• Window 클래스와 그것을 상속받은 WindowWIthScrollBars 클래스 예제

Page 23: Effective c++ 정리 chapter 4

Item 20: 객체 전달은 const T&

• Class Window{

public:

std::string name() const;

virtual void display() const;

};

• Class WindowWithScrollBars

:public Window{

public:

virtual void display() const;

};

Page 24: Effective c++ 정리 chapter 4

Item 20: 객체 전달은 const T&

• 윈도우의 이름을 출력하고 윈도우를 화면에 출력하는 함수

• void printNameAndDisplay(window w)

• 여기에 WindowWithScrollBars 객체를 전달

• WindowWithScrollBars wwsb;

printNameAndDisplay(wwsb); //복사 손실발생!

• 여기에서도 해결책은 Reference-to-Const

• void printNameAndDisplay(const Window& w);

• 기존 매개변수의 참조만 사용하므로 데이터를 유지한 상태로 사용됨.

Page 25: Effective c++ 정리 chapter 4

Item 20: 객체 전달은 const T&

• const T&가 만능은 아니다.

• 참조자는 근본적으로는 포인터로 구현된다.

• 전달 타입이 기본(primitive)타입인 경우 값 전달이 효율적이다.

• STL의 반복자와 함수객체에서도 동일한 convention이 적용

• 반복자와 함수객체의 복사 생성자/할당자의 효율을 고려해야 한다.

• 복사 손실 문제를 해결해야 한다.

Page 26: Effective c++ 정리 chapter 4

Item 20: 객체 전달은 const T&

• 기본 타입이 아닌 값 전달의 위험성

(크기 작다고 값 복사 하지마)

• 객체 크기가 작아도 복사 비용이 적은 것은 아님

• 포인터 타입의 깊은 복사의 경우

• 사용자 정의 타입은 연산 자체가 다르다.

• Double 하나만 있는 객체와 Double을 다루는 방식이 다름

• 기본 double은 레지스터 하나로 처리, 하지만 객체는 다르다!

• 포인터/참조는 무조건 레지스터 하나에 들어감

• 사용자 정의 타입의 크기는 변화가능하다.

Page 27: Effective c++ 정리 chapter 4

함수 반환에서참조반환을 피하자

ITEM 21

Page 28: Effective c++ 정리 chapter 4

Item 21: 함수 반환에서 참조 반환을 피하자.

• 참조 전달 만능주의에 빠지지 말자.

• 반환형이 참조일 때, 이미 소멸된 객체의 참조를 반환할 가능성

• Rational 클래스에서 friend 함수 operator*의 예시

class Rational {

friend

const Rational operator* (const Rational& lhs, const Rational& rhs);

}

Page 29: Effective c++ 정리 chapter 4

Item 21: 함수 반환에서 참조 반환을 피하자.

• 곱셈 결과를 값으로 반환하는 것이 정당한가?

• 참조자를 반환하면 생성과 소멸에 드는 비용을 줄일 수 있다.

• 참조자는 alias 즉 이미 존재하는 객체에 대한 참조

• 참조로 반환하는 경우 반환된 객체가 따로 어딘가에 존재해야 한다!

• Rational을 참조로 반환하고 싶다면

• Operator*에서 Rational 객체를 따로 생성해야 한다.

• 힙에 할당하거나, 스택에 생성하거나, 또는 static(^^)

Page 30: Effective c++ 정리 chapter 4

Item 21: 함수 반환에서 참조 반환을 피하자.

• 스택에 생성하는 경우

const Rational& operator* (const Rational& lhs,

const Rational& rhs)

{

Rational result(lhs.n * rhs.n, lhs.d * rhs.d); //반환값 스택에 생성

return result;

}

• 생성자 호출을 피하기위한 참조 반환이었지만 결국엔 생성해야한다.

• 반환된 참조의 대상은 함수 종료 후 소멸된다. (댕글링 참조자)

Page 31: Effective c++ 정리 chapter 4

Item 21: 함수 반환에서 참조 반환을 피하자.

• 힙에 생성하는 경우

const Rational& operator* (const Rational& lhs,

const Rational& rhs)

{

Rational* result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);

return *result;

}

• 여전히 생성자 호출의 비용은 그대로

• 할당한 메모리 해제를 어떻게 할 것인가?

Page 32: Effective c++ 정리 chapter 4

Item 21: 함수 반환에서 참조 반환을 피하자.

• 힙에 생성한 객체의 참조반환

• Rational w, x, y, z;

w = x * y * z; //operator*(operator*(x, y), z);

• 사용자 측에서 반환되는 참조자의 포인터에 접근할 방법이 없다.

• 함수 내에서는 해제하면 안 된다. (댕글링 참조자 문제)

• delete는 어디서 누가 어떻게 왜!!?

Page 33: Effective c++ 정리 chapter 4

Item 21: 함수 반환에서 참조 반환을 피하자.

• 마지막 발악. static으로 반환할 Rational을 정의한 경우

const Rational& operator* (const Rational& lhs,

const Rational& rhs)

{

static Rational result(lhs.n * rhs.n, lhs.d * rhs.d);

return result;

}

• Rational a, b, c, d;

If( (a * b) == (c * d) ) … //반환값이 static에 대한 참조이므로 항상 true

Page 34: Effective c++ 정리 chapter 4

Item 21: 함수 반환에서 참조 반환을 피하자.

• 가능하면 그냥 값 전달하자. 물론 정석은 있다.

inline const Rational operator* (const Rational& lhs,

const Rational& rhs)

{return Rational ( lhs.n * rhs.n, lhs.d * rhs.d );}

• inline 키워드를 사용하여 불필요한 생성/소멸자 호출 방지

• 컴파일러 최적화에 맡기자.

Page 35: Effective c++ 정리 chapter 4

데이터 멤버들은private에 선언하자

ITEM 22

Page 36: Effective c++ 정리 chapter 4

Item 22: 데이터 멤버들은 private에 선언하자

• Public 데이터 멤버 무엇이 문제인가?

• 사용자의 문법적 일관성을 해친다.

• 어떤 멤버는 데이터고 어떤 멤버는 함수다?

• 사용자가 객체에 접근 가능한 방법이 모두 함수인 편이 일관성이 높다.

• 사용자의 데이터 접근을 제어할 수 없다.

• 데이터 멤버에 대해 자유로운 접근이 가능하다.

• Private하면 직접 함수로 접근 방식을 커스터마이징 할 수 있다.

Page 37: Effective c++ 정리 chapter 4

Item 22: 데이터 멤버들은 private에 선언하자

• Public 데이터 멤버 무엇이 문제인가? 2

• 클래스의 온전한 캡슐화를 할 수 없다.

• 클래스 캡슐화의 좋은 예

class SpeedDataCollection {

public:

void addValue(int speed); //새로운 데이터 추가.

double averageSoFar() const; //평균 속도 반환

}

Page 38: Effective c++ 정리 chapter 4

Item 22: 데이터 멤버들은 private에 선언하자

• SpeedDataCollection 예시

• averageSoFar() 함수 어떻게 구현할 것인가?

• 속도 평균값을 저장하는 데이터 멤버를 유지하고 그 값을 반환한다.

• 성능 향성 but 객체 비대화

• 호출할 때마다 평균을 계산하여 반환한다.

• 성능 구림 but 작은 사이즈 객체

• 상황에 따라 적합한 방식으로 구현해야 한다.

Page 39: Effective c++ 정리 chapter 4

Item 22: 데이터 멤버들은 private에 선언하자

• SpeedDataCollection 예시

• 평균값에 대한 캡슐화가 이루어지지 않았다면?

• 구현방식이 바뀔 때마다 접근방식도 변경될 것이다.

• 캡슐화를 통해서 외부 객체는 항상 동일한 방식으로 원하는 결과를 받을 수 있다.

• 구현상의 융통성을 위한 캡슐화

• 함수를 통해 전달하기 때문에 데이터 전달 시 이벤트/동기화등 다양한 작업이 가능

• Public으로 선언된 멤버들을 변경하면 접근하는 방식을 손 봐야 한다는 의미

• 캡슐화는 클래스의 불변속성을 유지하는데 유리하다.

• 유일한 통로를 통해 접근하므로, 함부로 변경되는 것을 막을 수 있다.

Page 40: Effective c++ 정리 chapter 4

Item 22: 데이터 멤버들은 private에 선언하자

• Protected의 데이터 멤버 무엇이 문제인가?

• Public과 동일한 상황

• 데이터 멤버가 바뀌면 (제거되면) 깨지는 코드의 양과 캡슐화는 반비례

• 결국 protected인 데이터 멤버를 사용하는 모든 파생클래스들이 의존상태인 것.

• 부모 클래스의 protected인 데이터 멤버가 변경되면 많은 코드가 변경돼야 한다.

• 진짜 캡슐화를 하고 싶으면 데이터 멤버들은 private해라!

Page 41: Effective c++ 정리 chapter 4

비멤버 비프렌드 함수의장점ITEM 23

Page 42: Effective c++ 정리 chapter 4

Item 23: 비멤버 비프렌드 함수의 장점

• WebBrowser 클래스의 사례

• clearCache, History, Cookie를

한번에 하고 싶다면?

• 함수 clearEverything();

• 멤버 함수로 할 것인가?

• 비멤버 함수로 할 것인가?

class WebBrowser {

public:

...

void clearCache();

void clearHistory();

void removeCookies();

};

Page 43: Effective c++ 정리 chapter 4

Item 23: 비멤버 비프렌드 함수의 장점

• 캡슐화의 측면

• 캡슐화는 데이터에 접근 가능한 루트가 적을 수록 증가한다.

• 멤버함수는 객체의 private 데이터에 접근 가능하다.

• 비멤버 + 비프랜드 함수는 private 데이터에 접근 불가능하다.

• 캡슐화를 강화하는 것은 비멤버 + 비 프렌드 함수를 사용하는 것!

• 다른 비 프랜드 객체의 멤버함수를 사용하는 것도 훌륭한 선택이다.

Page 44: Effective c++ 정리 chapter 4

Item 23: 비멤버 비프렌드 함수의 장점

• 비 멤버 함수와 해당 객체를 같은 네임스페이스 안에 두자

• Namespace WebBrowserStuff {

class WebBrowser {…};

void clearBrowser (WebBrowser& wb);

}

• Utility 함수를 다루는 매우 훌륭한 방법

• 같은 네임스페이스를 여러 개의 소스로 나누어서 사용하는 것이 가능하다.

• 다양한 utility 기능들을 기능별로 헤더로 분리하여 선언하는 것이 가능

Page 45: Effective c++ 정리 chapter 4

Item 23: 비멤버 비프렌드 함수의 장점

• //in WebBrowser.h

Namespace WebBrowserStuff {

class WebBrowser { … };

}

//in WebBrowserBookmarks.h

Namespace WebBrowserStuff {

}

• Std 표준 라이브러리도 이런

형식으로 구성된다.

• 필요한 기능만 include 해서

사용할 수 있도록

• 실제로 사용하는 요소만 컴파

일 의존성을 고려하면 된다.

• 패키징 유연성!

Page 46: Effective c++ 정리 chapter 4

Item 23: 비멤버 비프렌드 함수의 장점

• Utility 함수 집합의 확장

• 해당 네임스페이스에 비멤버 비프랜드 함수를 추가하면 됨

• 새로운 헤더 만들고 네임스페이스에 원하는 함수들 추가하면 끝

• 캡슐화가 잘되고 확장도 쉬운 비멤버 비프렌드 함수 씁시다.

Page 47: Effective c++ 정리 chapter 4

멤버함수의 Blind SightITEM 24

Page 48: Effective c++ 정리 chapter 4

Item 24: 멤버함수의 Blind Sight

• 다시 Rational class (지겹다)

• 곱셈 연산을 지원하고 싶다.

• 멤버함수 / 비멤버 함수/ 비멤버 프렌드 함수 (선택지가 너무 많아)

• 객체 지향 파워로 돌파해보자.

• 곱셈 연산은 Rational 클래스 자체랑 관련이 있으니까

• 멤버함수로 넣어주는 게 적절할 것만 같아.

• Item 23에서는 캡슐화가 어쩌고 했지만 넘어가도록 하자

Page 49: Effective c++ 정리 chapter 4

Item 24: 멤버함수의 Blind Sight

• const Rational operator* (const Rational& rhs);

• Rational oneEight (1, 8);

Rational oneHalf (1, 2);

Rational result = oneHalf * oneEight;

result = result * oneEight;

• 자~알 돌아간다.

Page 50: Effective c++ 정리 chapter 4

Item 24: 멤버함수의 Blind Sight

• const Rational Rational::operator* (const Rational& rhs);

클래스 멤버함수로 operator*를 정의하자

• Rational oneEight (1, 8);

Rational oneHalf (1, 2);

Rational result = oneHalf * oneEight;

result = result * oneEight;

• 자~알 돌아간다.

Page 51: Effective c++ 정리 chapter 4

Item 24: 멤버함수의 Blind Sight

• 혼합형 수치 연산하고 싶다. 헠헠 Int * Rational 같은거…

result = oneHalf * 2; //OK

result = 2 * oneHalf //에러?! 왜?!

• 문제의 원인

result = oneHalf. operator*(2); //Rational에서 메소드 호출

result = 2. operator*(oneHalf); //Int에서 메소드 호출??

Page 52: Effective c++ 정리 chapter 4

Item 24: 멤버함수의 Blind Sight

• 다른 방법의 돌파구

result = operator*(2, oneHalf); //에러!?

• Rational::operator*는 좌변에 반드시 this를 사용하기 때문!

• 우변은 암시적 형변환으로 자연스러운 처리

Rational temp(2);

result = oneHalf.operator*( temp );

• 생성자가 explicit였으면 형변환 불가능으로 이마저도 에러

Page 53: Effective c++ 정리 chapter 4

Item 24: 멤버함수의 Blind Sight

• 양변에 모두 암시적 형변환을 지원하고 싶다면?

• 멤버 함수로는 안된다.

• 비멤버 함수로 처리

const Rational operator*(const Rational& lhs, const Rational& rhs);

{//좌변과 우변을 모두 설정가능

return Rational(lhs.numerator() * rhs.numerator(), //캡슐화된 접근

lhs.denominator() * rhs.denominator());

}

Page 54: Effective c++ 정리 chapter 4

Item 24: 멤버함수의 Blind Sight

• 완벽하게 캡슐화된 접근방식으로 operator*를 구현가능하다.

• 쓸데없이 friend 선언해서 캡슐화를 줄이지 말자.

• 객체를 다루는 함수는 가능하면 비 friend 함수로 캡슐화된 접근을 통

해 문제를 해결하자.

Page 55: Effective c++ 정리 chapter 4

예외처리 없는 swapITEM 25

Page 56: Effective c++ 정리 chapter 4

Item 25: 예외 처리 없는 swap

• Swap의 중요성

• 예외 안전성 프로그래밍에 반드시 필요한 역할 수행

• 자기 대입 현상에 대처하는데 핵심적 역할 (item 11참조)

• STL에 추가됨

• 잘 동작하는 swap을 만들어 두자.

Page 57: Effective c++ 정리 chapter 4

Item 25: 예외 처리 없는 swap

• 표준의 std::swap

• 전형적인 구현

• T가 복사연산만 지원한다면 제

대로 동작한다.

• 세 번의 복사 연산 비용

• 썩 훌륭해 보이지 않는다.

• Template<typename T>

void swap(T& a, T& b)

{

T temp(a);

a = b;

b = temp;

}

Page 58: Effective c++ 정리 chapter 4

Item 25: 예외 처리 없는 swap

• Pimple Idiom 사용하는 객체의 swap

• Pimple Idiom은 (http://ozt88.tistory.com/32 참조)

• Swap에서 Impl*의 포인터 pImpl의 주소값만 맞바꿔주면 충분하다.

• 하지만 std::swap의 코드대로라면 깊은 복사를 3번 수행한다.

• 초 비효율

Page 59: Effective c++ 정리 chapter 4

Item 25: 예외 처리 없는 swap

• std::swap에 템플릿 특수화를 더하여 해결하자

• Pimple Idiom을 사용하는 객체 Widget에 swap() 메소드 준비

• template<>

void swap<Widget>(Widget& a, Widget& b){

a.swap(b);

}

Page 60: Effective c++ 정리 chapter 4

Item 25: 예외 처리 없는 swap

• 좀더 확장해보자.

• Pimpl에 들어가는 데이터 타입을 매개변수로 받는 클래스 템플릿

• Widget<T>

template<typename T>

void swap<Widget<T>>(Widget<T>& a, Widget<T>& b)

{ a. swap( b ); } //에러??!!

• 함수 템플릿에서 부분 특수화를 허용하지 않는다.

• 클래스 템플릿은 허용한다.

Page 61: Effective c++ 정리 chapter 4

Item 25: 예외 처리 없는 swap

• 부분 특수화 대신 swap을 오버로딩 하는 방법으로 해결가능

template<typename T>

void swap(Widget<T>& a, Widget<T>& b)

{ a.swap(b); } //템플릿 특수화를 버리고 일반 오버로딩을 사용

• 하지만 이 코드는 제대로 동작하지 않는다.

• std에 새로운 함수를 추가하는 것이 불가능하기 때문

• std 밖에서도 쉽게 사용할 수 있는 방법은?

Page 62: Effective c++ 정리 chapter 4

Item 25: 예외 처리 없는 swap

• Widget<T>와 오버로딩할

swap함수를 같은 네임스페이스

에 선언한다.

• 컴파일러의 이름탐색 규칙에 따

라서 같은 네임스페이스 안에

있는 swap이 먼저 호출된다.

namespace WidgetStuff {

template<typename T>

class Widget { … };

template<typename T>

void swap(Widget<T>& a ,

Widget<T>& b);

}

Page 63: Effective c++ 정리 chapter 4

Item 25: 예외 처리 없는 swap

• 사용자가 swap을 쓰려면?

• 타입 전용 swap이 있는지 없는지도

모르는 상황

• 오른쪽 처럼 쓰면 OK

• using std::swap

• 표준 swap쓸 수 있게 준비

• 실제 swap 호출에서 먼저 이름탐색 규

칙에 따라 T나 네임스페이스에 맞는

swap을 먼저 찾고 없으면 std::swap 씀

Template<typename T>

void doSomething(T& ob1, T&

ob2)

{

using std::swap;

swap(ob1, ob2);

}

Page 64: Effective c++ 정리 chapter 4

Item 25: 예외 처리 없는 swap

• 요점정리

• Swap 효율이 나쁘지 않으면 그냥 써라

• 따로 Swap을 만들어 쓰고싶다면

• 해당 타입의 public 멤버함수로 원하는 swap을 정의해라

• 클래스 또는 템플릿이 있는 네임스페이스에 멤버함수 swap을 호출하는

비멤버 swap을 만들어 넣어라.

• 일반 클래스에대한 특수화는 std::swap에서 템플릿 특수화하는 것으로 충분하다.

• swap 호출전에 using std::swap;을 선언하자.