SlideShare ist ein Scribd-Unternehmen logo
1 von 64
EFFECTIVE C++ 정리
Chapter 4
설계 및 선언
잘쓰기는 쉽게
못쓰기는 어렵게
ITEM 18
Item 18: 잘쓰기 쉽게 못쓰기 어렵게
• 인터페이스
• 유저와 코드가 접하는 경로
• 잘쓰기는 쉽게 못쓰기는 어렵게 만들자
• 유저가 생각하는 대로 동작하게 하자.
• 잘못 쓴 경우 뭔가 항의의 몸부림을 치자.
• 사용자가 실수하는 경우를 머리에 두고 있자.
Item 18: 잘쓰기 쉽게 못쓰기 어렵게
• 날짜 클래스의 생성자
Date( int month, int day, int year);
• 매개변수 순서를 잘못 입력한 경우
Data d(30, 3, 1995); //3, 30이 올바른 사용
• 가능한 범위를 넘어가는 경우
Data d(3, 40, 1995); //3월 40일은 없다.
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); //올바른 사용
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해서 안전하게
Item 18: 잘쓰기 쉽게 못쓰기 어렵게
• 또 다른 제약 붙이기 const (item 3 참조)
• operator*의 반환 타입을 const로 한정한 이유
• if( a * b = c )… //이런 실수를 막기 위해서
• 별다른 이유가 없다면 클래스는 기본 타입처럼 동작하게 하자
• 기존 int 역시 *연산에 다른 값을 대입할 수 없게 만들어져있음.
• 유사한 쓰임새의 기본타입의 동작을 모방하자
• 일관성 있는 인터페이스의 제공
Item 18: 잘쓰기 쉽게 못쓰기 어렵게
• STL에서 나타나는 비일관성의 예
• 데이터의 길이 또는 크기를 나타낼 떄
• Array는 length 프로퍼티로
• String은 length 메서드로
• List는 size 메서드로
• 비 일관성은 인터페이스를 해친다.
Item 18: 잘쓰기 쉽게 못쓰기 어렵게
• 사용자 쪽에서 뭔가 외워야 하는 인터페이스는 잘못 되었다.
• Investment* createInvestment(); //팩토리 함수 (in item 13)
• 사용자 측에서 delete하는 것을 잊어서는 안 된다.
• 사용자 측에서 별 고민 없이 그냥 사용할 수 있게
• std::shared_ptr<Investment> createInvestment(); //스마트포인터 반환
• 자동 해제되어 편하게 사용가능
• 사용자 실수를 원천 봉쇄 ( deleter도 설계자가 지정 가능 )
Item 18: 잘쓰기 쉽게 못쓰기 어렵게
• 사용자 쪽에서 뭔가 외워야 하는 인터페이스는 잘못 되었다.
• Investment* createInvestment(); //팩토리 함수 (in item 13)
• 사용자 측에서 delete하는 것을 잊어서는 안 된다.
• 사용자 측에서 별 고민 없이 그냥 사용할 수 있게
• std::shared_ptr<Investment> createInvestment(); //스마트포인터 반환
• 자동 해제되어 편하게 사용가능
• 사용자 실수를 원천 봉쇄 ( deleter도 설계자가 지정 가능 )
Item 18: 잘쓰기 쉽게 못쓰기 어렵게
• 교차 DLL 문제
• 자원 할당된 포인터가 여기저기 돌아다니는 경우
• 객체 생성시에 특정 DLL에 있는 new를 사용
• 해제 시에는 다른 DLL에 있는 delete 를 사용
• tr1::std::shared_ptr은 그런 걱정 ㄴㄴ
• Shared_ptr은 자신의 deleter를 control block에서 관리하기 때문
• 훌륭한 인터페이스의 대표적 사례 (http://ozt88.tistory.com/28 참조)
클래스 설계는
타입을 설계하듯
ITEM 19
Item 19: 클래스 설계는 타입을 설계하듯
• 클래스를 설계할 때, 새로운 언어의 타입을 설계하듯
• 자연스러운 구문, 의미체계의 구축
• 직관적이며 동시에 효율적인 기능 구현
• 고려사항들 1
• 객체 생성 소멸 방식
• 객체 초기화와 대입연산의 차이
• 값 복사의 의미와 구문
Item 19: 클래스 설계는 타입을 설계하듯
• 고려사항들 2
• 타입의 domain 설정
• 값 제약
• Setter들에서 조건 체크
• 멤버 상속여부
• 상속 받았다면 어떤 멤버의 속성을 상속받아 사용할 것인가?
• 상속해 줄 것이라면 어떤 멤버를 가상으로 설정할 것인가?
Item 19: 클래스 설계는 타입을 설계하듯
• 고려사항들 3
• 타입 변환
• 어떤 타입들로 변환을 허락할 것인가
• 암시적 변환/ 명시적 변환
• 적당한 연산자와 멤버 함수 선정
• special function 중 어떤 것을 취사 선택하여 사용할 것인가?
• Private로 봉인 가능
• 멤버함수 접근 권한 설정 (public/private/protected)
Item 19: 클래스 설계는 타입을 설계하듯
• 고려사항들 4
• 선언되지 않은 인터페이스
• 보장할 자원, 성능, 예외 안정성
• 일반적 템플릿을 만들어야 하는가?
• 정말로 필요한 타입인가?
• 비슷한 클래스가 있고 기능이 몇 개 추가되지 않는다면
• 일반 비멤버 함수를 만들거나 템플릿을 몇 개 더 정의하는 게 낫다.
객체 전달은 const T&
ITEM 20
Item 20: 객체 전달은 const T&
• C++ 함수의 동작은 기본적으로 값에 의한 전달
• 일반 객체 paramete는 argumen에 대한 사본으로 생성
• 반환 값의 사본을 만들어 반환 객체를 받는다.
• 예제 코드 Person class와 그를 상속받는 Student Class
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;
};
Item 20: 객체 전달은 const T&
• Student를 인자로 전달받는 함수 validateStudent
bool validataStudent(Student s);
…
Student plato;
bool platoIsOK = validateStudent(plato);
• 객체 전달에 필요한 비용
• Parameter의 Student 복사 생성자 호출
• Argument의 Student 소멸자 호출
Item 20: 객체 전달은 const T&
• 좀더 구체적인 비용
• Student 생성할 때 덩달아 불리는 생성자들
• 멤버변수 SchoolName 생성자, SchoolAddress 생성자 호출
• 상속받은 Person의 생성자 호출
• Person의 멤버변수 address, name 생성자 호출
• Student 해제할때 덩달아 호출되는 소멸자들
• 위에서 생성된 모든 객체들 각각 해제
• TOTAL : 생성자 6번, 소멸자 6번
Item 20: 객체 전달은 const T&
• 비효율적인 값 전달의 대체제 const T&
• Reference-to-Const
• 어차피 값으로 전달하면, 기존 객체의 값이 바뀔 일이 없다는 의미
• 상수성이 유지되면 사본을 만드는 대신 상수 객체의 참조를 넘기는게 이득
• No 생성 No 소멸 No 복사
• bool validateStudent( const Student& s );
• 복사손실 문제를 해결
• 정적 타입이 부모 클래스인 경우, 부모클래스의 복사 생성자 호출로 데이터 손실
• Window 클래스와 그것을 상속받은 WindowWIthScrollBars 클래스 예제
Item 20: 객체 전달은 const T&
• Class Window{
public:
…
std::string name() const;
virtual void display() const;
};
• Class WindowWithScrollBars
:public Window{
public:
…
virtual void display() const;
};
Item 20: 객체 전달은 const T&
• 윈도우의 이름을 출력하고 윈도우를 화면에 출력하는 함수
• void printNameAndDisplay(window w)
• 여기에 WindowWithScrollBars 객체를 전달
• WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb); //복사 손실발생!
• 여기에서도 해결책은 Reference-to-Const
• void printNameAndDisplay(const Window& w);
• 기존 매개변수의 참조만 사용하므로 데이터를 유지한 상태로 사용됨.
Item 20: 객체 전달은 const T&
• const T&가 만능은 아니다.
• 참조자는 근본적으로는 포인터로 구현된다.
• 전달 타입이 기본(primitive)타입인 경우 값 전달이 효율적이다.
• STL의 반복자와 함수객체에서도 동일한 convention이 적용
• 반복자와 함수객체의 복사 생성자/할당자의 효율을 고려해야 한다.
• 복사 손실 문제를 해결해야 한다.
Item 20: 객체 전달은 const T&
• 기본 타입이 아닌 값 전달의 위험성
(크기 작다고 값 복사 하지마)
• 객체 크기가 작아도 복사 비용이 적은 것은 아님
• 포인터 타입의 깊은 복사의 경우
• 사용자 정의 타입은 연산 자체가 다르다.
• Double 하나만 있는 객체와 Double을 다루는 방식이 다름
• 기본 double은 레지스터 하나로 처리, 하지만 객체는 다르다!
• 포인터/참조는 무조건 레지스터 하나에 들어감
• 사용자 정의 타입의 크기는 변화가능하다.
함수 반환에서
참조반환을 피하자
ITEM 21
Item 21: 함수 반환에서 참조 반환을 피하자.
• 참조 전달 만능주의에 빠지지 말자.
• 반환형이 참조일 때, 이미 소멸된 객체의 참조를 반환할 가능성
• Rational 클래스에서 friend 함수 operator*의 예시
class Rational {
…
friend
const Rational operator* (const Rational& lhs, const Rational& rhs);
}
Item 21: 함수 반환에서 참조 반환을 피하자.
• 곱셈 결과를 값으로 반환하는 것이 정당한가?
• 참조자를 반환하면 생성과 소멸에 드는 비용을 줄일 수 있다.
• 참조자는 alias 즉 이미 존재하는 객체에 대한 참조
• 참조로 반환하는 경우 반환된 객체가 따로 어딘가에 존재해야 한다!
• Rational을 참조로 반환하고 싶다면
• Operator*에서 Rational 객체를 따로 생성해야 한다.
• 힙에 할당하거나, 스택에 생성하거나, 또는 static(^^)
Item 21: 함수 반환에서 참조 반환을 피하자.
• 스택에 생성하는 경우
const Rational& operator* (const Rational& lhs,
const Rational& rhs)
{
Rational result(lhs.n * rhs.n, lhs.d * rhs.d); //반환값 스택에 생성
return result;
}
• 생성자 호출을 피하기위한 참조 반환이었지만 결국엔 생성해야한다.
• 반환된 참조의 대상은 함수 종료 후 소멸된다. (댕글링 참조자)
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;
}
• 여전히 생성자 호출의 비용은 그대로
• 할당한 메모리 해제를 어떻게 할 것인가?
Item 21: 함수 반환에서 참조 반환을 피하자.
• 힙에 생성한 객체의 참조반환
• Rational w, x, y, z;
w = x * y * z; //operator*(operator*(x, y), z);
• 사용자 측에서 반환되는 참조자의 포인터에 접근할 방법이 없다.
• 함수 내에서는 해제하면 안 된다. (댕글링 참조자 문제)
• delete는 어디서 누가 어떻게 왜!!?
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
Item 21: 함수 반환에서 참조 반환을 피하자.
• 가능하면 그냥 값 전달하자. 물론 정석은 있다.
inline const Rational operator* (const Rational& lhs,
const Rational& rhs)
{return Rational ( lhs.n * rhs.n, lhs.d * rhs.d );}
• inline 키워드를 사용하여 불필요한 생성/소멸자 호출 방지
• 컴파일러 최적화에 맡기자.
데이터 멤버들은
private에 선언하자
ITEM 22
Item 22: 데이터 멤버들은 private에 선언하자
• Public 데이터 멤버 무엇이 문제인가?
• 사용자의 문법적 일관성을 해친다.
• 어떤 멤버는 데이터고 어떤 멤버는 함수다?
• 사용자가 객체에 접근 가능한 방법이 모두 함수인 편이 일관성이 높다.
• 사용자의 데이터 접근을 제어할 수 없다.
• 데이터 멤버에 대해 자유로운 접근이 가능하다.
• Private하면 직접 함수로 접근 방식을 커스터마이징 할 수 있다.
Item 22: 데이터 멤버들은 private에 선언하자
• Public 데이터 멤버 무엇이 문제인가? 2
• 클래스의 온전한 캡슐화를 할 수 없다.
• 클래스 캡슐화의 좋은 예
class SpeedDataCollection {
public:
void addValue(int speed); //새로운 데이터 추가.
double averageSoFar() const; //평균 속도 반환
}
Item 22: 데이터 멤버들은 private에 선언하자
• SpeedDataCollection 예시
• averageSoFar() 함수 어떻게 구현할 것인가?
• 속도 평균값을 저장하는 데이터 멤버를 유지하고 그 값을 반환한다.
• 성능 향성 but 객체 비대화
• 호출할 때마다 평균을 계산하여 반환한다.
• 성능 구림 but 작은 사이즈 객체
• 상황에 따라 적합한 방식으로 구현해야 한다.
Item 22: 데이터 멤버들은 private에 선언하자
• SpeedDataCollection 예시
• 평균값에 대한 캡슐화가 이루어지지 않았다면?
• 구현방식이 바뀔 때마다 접근방식도 변경될 것이다.
• 캡슐화를 통해서 외부 객체는 항상 동일한 방식으로 원하는 결과를 받을 수 있다.
• 구현상의 융통성을 위한 캡슐화
• 함수를 통해 전달하기 때문에 데이터 전달 시 이벤트/동기화등 다양한 작업이 가능
• Public으로 선언된 멤버들을 변경하면 접근하는 방식을 손 봐야 한다는 의미
• 캡슐화는 클래스의 불변속성을 유지하는데 유리하다.
• 유일한 통로를 통해 접근하므로, 함부로 변경되는 것을 막을 수 있다.
Item 22: 데이터 멤버들은 private에 선언하자
• Protected의 데이터 멤버 무엇이 문제인가?
• Public과 동일한 상황
• 데이터 멤버가 바뀌면 (제거되면) 깨지는 코드의 양과 캡슐화는 반비례
• 결국 protected인 데이터 멤버를 사용하는 모든 파생클래스들이 의존상태인 것.
• 부모 클래스의 protected인 데이터 멤버가 변경되면 많은 코드가 변경돼야 한다.
• 진짜 캡슐화를 하고 싶으면 데이터 멤버들은 private해라!
비멤버 비프렌드 함수의
장점
ITEM 23
Item 23: 비멤버 비프렌드 함수의 장점
• WebBrowser 클래스의 사례
• clearCache, History, Cookie를
한번에 하고 싶다면?
• 함수 clearEverything();
• 멤버 함수로 할 것인가?
• 비멤버 함수로 할 것인가?
class WebBrowser {
public:
...
void clearCache();
void clearHistory();
void removeCookies();
};
Item 23: 비멤버 비프렌드 함수의 장점
• 캡슐화의 측면
• 캡슐화는 데이터에 접근 가능한 루트가 적을 수록 증가한다.
• 멤버함수는 객체의 private 데이터에 접근 가능하다.
• 비멤버 + 비프랜드 함수는 private 데이터에 접근 불가능하다.
• 캡슐화를 강화하는 것은 비멤버 + 비 프렌드 함수를 사용하는 것!
• 다른 비 프랜드 객체의 멤버함수를 사용하는 것도 훌륭한 선택이다.
Item 23: 비멤버 비프렌드 함수의 장점
• 비 멤버 함수와 해당 객체를 같은 네임스페이스 안에 두자
• Namespace WebBrowserStuff {
class WebBrowser {…};
void clearBrowser (WebBrowser& wb);
}
• Utility 함수를 다루는 매우 훌륭한 방법
• 같은 네임스페이스를 여러 개의 소스로 나누어서 사용하는 것이 가능하다.
• 다양한 utility 기능들을 기능별로 헤더로 분리하여 선언하는 것이 가능
Item 23: 비멤버 비프렌드 함수의 장점
• //in WebBrowser.h
Namespace WebBrowserStuff {
class WebBrowser { … };
}
//in WebBrowserBookmarks.h
Namespace WebBrowserStuff {
…
}
• Std 표준 라이브러리도 이런
형식으로 구성된다.
• 필요한 기능만 include 해서
사용할 수 있도록
• 실제로 사용하는 요소만 컴파
일 의존성을 고려하면 된다.
• 패키징 유연성!
Item 23: 비멤버 비프렌드 함수의 장점
• Utility 함수 집합의 확장
• 해당 네임스페이스에 비멤버 비프랜드 함수를 추가하면 됨
• 새로운 헤더 만들고 네임스페이스에 원하는 함수들 추가하면 끝
• 캡슐화가 잘되고 확장도 쉬운 비멤버 비프렌드 함수 씁시다.
멤버함수의 Blind Sight
ITEM 24
Item 24: 멤버함수의 Blind Sight
• 다시 Rational class (지겹다)
• 곱셈 연산을 지원하고 싶다.
• 멤버함수 / 비멤버 함수/ 비멤버 프렌드 함수 (선택지가 너무 많아)
• 객체 지향 파워로 돌파해보자.
• 곱셈 연산은 Rational 클래스 자체랑 관련이 있으니까
• 멤버함수로 넣어주는 게 적절할 것만 같아.
• Item 23에서는 캡슐화가 어쩌고 했지만 넘어가도록 하자
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;
• 자~알 돌아간다.
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;
• 자~알 돌아간다.
Item 24: 멤버함수의 Blind Sight
• 혼합형 수치 연산하고 싶다. 헠헠 Int * Rational 같은거…
result = oneHalf * 2; //OK
result = 2 * oneHalf //에러?! 왜?!
• 문제의 원인
result = oneHalf. operator*(2); //Rational에서 메소드 호출
result = 2. operator*(oneHalf); //Int에서 메소드 호출??
Item 24: 멤버함수의 Blind Sight
• 다른 방법의 돌파구
result = operator*(2, oneHalf); //에러!?
• Rational::operator*는 좌변에 반드시 this를 사용하기 때문!
• 우변은 암시적 형변환으로 자연스러운 처리
Rational temp(2);
result = oneHalf.operator*( temp );
• 생성자가 explicit였으면 형변환 불가능으로 이마저도 에러
Item 24: 멤버함수의 Blind Sight
• 양변에 모두 암시적 형변환을 지원하고 싶다면?
• 멤버 함수로는 안된다.
• 비멤버 함수로 처리
const Rational operator*(const Rational& lhs, const Rational& rhs);
{//좌변과 우변을 모두 설정가능
return Rational(lhs.numerator() * rhs.numerator(), //캡슐화된 접근
lhs.denominator() * rhs.denominator());
}
Item 24: 멤버함수의 Blind Sight
• 완벽하게 캡슐화된 접근방식으로 operator*를 구현가능하다.
• 쓸데없이 friend 선언해서 캡슐화를 줄이지 말자.
• 객체를 다루는 함수는 가능하면 비 friend 함수로 캡슐화된 접근을 통
해 문제를 해결하자.
예외처리 없는 swap
ITEM 25
Item 25: 예외 처리 없는 swap
• Swap의 중요성
• 예외 안전성 프로그래밍에 반드시 필요한 역할 수행
• 자기 대입 현상에 대처하는데 핵심적 역할 (item 11참조)
• STL에 추가됨
• 잘 동작하는 swap을 만들어 두자.
Item 25: 예외 처리 없는 swap
• 표준의 std::swap
• 전형적인 구현
• T가 복사연산만 지원한다면 제
대로 동작한다.
• 세 번의 복사 연산 비용
• 썩 훌륭해 보이지 않는다.
• Template<typename T>
void swap(T& a, T& b)
{
T temp(a);
a = b;
b = temp;
}
Item 25: 예외 처리 없는 swap
• Pimple Idiom 사용하는 객체의 swap
• Pimple Idiom은 (http://ozt88.tistory.com/32 참조)
• Swap에서 Impl*의 포인터 pImpl의 주소값만 맞바꿔주면 충분하다.
• 하지만 std::swap의 코드대로라면 깊은 복사를 3번 수행한다.
• 초 비효율
Item 25: 예외 처리 없는 swap
• std::swap에 템플릿 특수화를 더하여 해결하자
• Pimple Idiom을 사용하는 객체 Widget에 swap() 메소드 준비
• template<>
void swap<Widget>(Widget& a, Widget& b){
a.swap(b);
}
Item 25: 예외 처리 없는 swap
• 좀더 확장해보자.
• Pimpl에 들어가는 데이터 타입을 매개변수로 받는 클래스 템플릿
• Widget<T>
template<typename T>
void swap<Widget<T>>(Widget<T>& a, Widget<T>& b)
{ a. swap( b ); } //에러??!!
• 함수 템플릿에서 부분 특수화를 허용하지 않는다.
• 클래스 템플릿은 허용한다.
Item 25: 예외 처리 없는 swap
• 부분 특수화 대신 swap을 오버로딩 하는 방법으로 해결가능
template<typename T>
void swap(Widget<T>& a, Widget<T>& b)
{ a.swap(b); } //템플릿 특수화를 버리고 일반 오버로딩을 사용
• 하지만 이 코드는 제대로 동작하지 않는다.
• std에 새로운 함수를 추가하는 것이 불가능하기 때문
• std 밖에서도 쉽게 사용할 수 있는 방법은?
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);
…
}
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);
}
Item 25: 예외 처리 없는 swap
• 요점정리
• Swap 효율이 나쁘지 않으면 그냥 써라
• 따로 Swap을 만들어 쓰고싶다면
• 해당 타입의 public 멤버함수로 원하는 swap을 정의해라
• 클래스 또는 템플릿이 있는 네임스페이스에 멤버함수 swap을 호출하는
비멤버 swap을 만들어 넣어라.
• 일반 클래스에대한 특수화는 std::swap에서 템플릿 특수화하는 것으로 충분하다.
• swap 호출전에 using std::swap;을 선언하자.

Weitere ähnliche Inhalte

Was ist angesagt?

effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리Injae Lee
 
Effective c++chapter3
Effective c++chapter3Effective c++chapter3
Effective c++chapter3성연 김
 
Effective c++chapter8
Effective c++chapter8Effective c++chapter8
Effective c++chapter8성연 김
 
Effective c++chapter1 and2
Effective c++chapter1 and2Effective c++chapter1 and2
Effective c++chapter1 and2성연 김
 
Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4성연 김
 
C++ Advanced 강의 4주차
 C++ Advanced 강의 4주차 C++ Advanced 강의 4주차
C++ Advanced 강의 4주차HyunJoon Park
 
이펙티브 C++ 공부
이펙티브 C++ 공부이펙티브 C++ 공부
이펙티브 C++ 공부quxn6
 
C++ Advanced 강의 1주차
C++ Advanced 강의 1주차C++ Advanced 강의 1주차
C++ Advanced 강의 1주차HyunJoon Park
 
이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디quxn6
 
More effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshinMore effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshinDong Chan Shin
 
Mec++ chapter3,4
Mec++ chapter3,4Mec++ chapter3,4
Mec++ chapter3,4문익 장
 
More effective c++ 2
More effective c++ 2More effective c++ 2
More effective c++ 2현찬 양
 
More effective c++ 항목30부터
More effective c++ 항목30부터More effective c++ 항목30부터
More effective c++ 항목30부터Dong Chan Shin
 
Effective c++ chapter3, 4 요약본
Effective c++ chapter3, 4 요약본Effective c++ chapter3, 4 요약본
Effective c++ chapter3, 4 요약본Dong Chan Shin
 
Effective c++ 1,2
Effective c++ 1,2Effective c++ 1,2
Effective c++ 1,2세빈 정
 
이펙티브 C++ 스터디
이펙티브 C++ 스터디이펙티브 C++ 스터디
이펙티브 C++ 스터디quxn6
 
Effective c++ 4
Effective c++ 4Effective c++ 4
Effective c++ 4현찬 양
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)익성 조
 
Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉HyunJoon Park
 
More effective c++ 3
More effective c++ 3More effective c++ 3
More effective c++ 3현찬 양
 

Was ist angesagt? (20)

effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리
 
Effective c++chapter3
Effective c++chapter3Effective c++chapter3
Effective c++chapter3
 
Effective c++chapter8
Effective c++chapter8Effective c++chapter8
Effective c++chapter8
 
Effective c++chapter1 and2
Effective c++chapter1 and2Effective c++chapter1 and2
Effective c++chapter1 and2
 
Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4
 
C++ Advanced 강의 4주차
 C++ Advanced 강의 4주차 C++ Advanced 강의 4주차
C++ Advanced 강의 4주차
 
이펙티브 C++ 공부
이펙티브 C++ 공부이펙티브 C++ 공부
이펙티브 C++ 공부
 
C++ Advanced 강의 1주차
C++ Advanced 강의 1주차C++ Advanced 강의 1주차
C++ Advanced 강의 1주차
 
이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디
 
More effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshinMore effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshin
 
Mec++ chapter3,4
Mec++ chapter3,4Mec++ chapter3,4
Mec++ chapter3,4
 
More effective c++ 2
More effective c++ 2More effective c++ 2
More effective c++ 2
 
More effective c++ 항목30부터
More effective c++ 항목30부터More effective c++ 항목30부터
More effective c++ 항목30부터
 
Effective c++ chapter3, 4 요약본
Effective c++ chapter3, 4 요약본Effective c++ chapter3, 4 요약본
Effective c++ chapter3, 4 요약본
 
Effective c++ 1,2
Effective c++ 1,2Effective c++ 1,2
Effective c++ 1,2
 
이펙티브 C++ 스터디
이펙티브 C++ 스터디이펙티브 C++ 스터디
이펙티브 C++ 스터디
 
Effective c++ 4
Effective c++ 4Effective c++ 4
Effective c++ 4
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)
 
Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉
 
More effective c++ 3
More effective c++ 3More effective c++ 3
More effective c++ 3
 

Andere mochten auch

Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...Seok-joon Yun
 
Modern effective c++ 항목 3
Modern effective c++ 항목 3Modern effective c++ 항목 3
Modern effective c++ 항목 3ssuser7c5a40
 
TCP가 실패하는 상황들
TCP가 실패하는 상황들TCP가 실패하는 상황들
TCP가 실패하는 상황들ssuser7c5a40
 
Effective c++ 2
Effective c++ 2Effective c++ 2
Effective c++ 2현찬 양
 
C++정리 스마트포인터
C++정리 스마트포인터C++정리 스마트포인터
C++정리 스마트포인터fefe7270
 
Stagefright recorder part1
Stagefright recorder part1Stagefright recorder part1
Stagefright recorder part1fefe7270
 
실전프로젝트 정서경 양현찬
실전프로젝트 정서경 양현찬실전프로젝트 정서경 양현찬
실전프로젝트 정서경 양현찬현찬 양
 
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산Taeung Ra
 

Andere mochten auch (10)

Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
 
Modern effective cpp 항목1
Modern effective cpp 항목1Modern effective cpp 항목1
Modern effective cpp 항목1
 
Modern effective c++ 항목 3
Modern effective c++ 항목 3Modern effective c++ 항목 3
Modern effective c++ 항목 3
 
TCP가 실패하는 상황들
TCP가 실패하는 상황들TCP가 실패하는 상황들
TCP가 실패하는 상황들
 
Effective c++ 2
Effective c++ 2Effective c++ 2
Effective c++ 2
 
C++정리 스마트포인터
C++정리 스마트포인터C++정리 스마트포인터
C++정리 스마트포인터
 
Stagefright recorder part1
Stagefright recorder part1Stagefright recorder part1
Stagefright recorder part1
 
Titanic with r
Titanic with rTitanic with r
Titanic with r
 
실전프로젝트 정서경 양현찬
실전프로젝트 정서경 양현찬실전프로젝트 정서경 양현찬
실전프로젝트 정서경 양현찬
 
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
 

Ähnlich wie Effective c++ 정리 chapter 4

Effective c++ 1~8장
Effective c++ 1~8장 Effective c++ 1~8장
Effective c++ 1~8장 Shin heemin
 
Chapter7~9 ppt
Chapter7~9 pptChapter7~9 ppt
Chapter7~9 pptInjae Lee
 
Effective STL 1~4장 정리
Effective STL 1~4장 정리Effective STL 1~4장 정리
Effective STL 1~4장 정리Shin heemin
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinDong Chan Shin
 
More effective c++ chapter1,2
More effective c++ chapter1,2More effective c++ chapter1,2
More effective c++ chapter1,2문익 장
 
Chapter5 ~ 6
Chapter5 ~ 6Chapter5 ~ 6
Chapter5 ~ 6Injae Lee
 
HolubOnPatterns/chapter2_2
HolubOnPatterns/chapter2_2HolubOnPatterns/chapter2_2
HolubOnPatterns/chapter2_2SeungHyun Hwang
 
9 object class
9 object class9 object class
9 object class웅식 전
 
Effective c++(chapter 5,6)
Effective c++(chapter 5,6)Effective c++(chapter 5,6)
Effective c++(chapter 5,6)문익 장
 
Effective c++(chapter3,4)
Effective c++(chapter3,4)Effective c++(chapter3,4)
Effective c++(chapter3,4)문익 장
 
Effective java
Effective javaEffective java
Effective javaHaeil Yi
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.Ryan Park
 
Mec chapter 5,6
Mec chapter 5,6Mec chapter 5,6
Mec chapter 5,6문익 장
 
Let's Go (golang)
Let's Go (golang)Let's Go (golang)
Let's Go (golang)상욱 송
 
Effective c++ Chapter1,2
Effective c++ Chapter1,2Effective c++ Chapter1,2
Effective c++ Chapter1,2문익 장
 
Effective c++ chapter 7,8
Effective c++ chapter 7,8Effective c++ chapter 7,8
Effective c++ chapter 7,8문익 장
 
Effective c++ chapter7_8_9_dcshin
Effective c++ chapter7_8_9_dcshinEffective c++ chapter7_8_9_dcshin
Effective c++ chapter7_8_9_dcshinDong Chan Shin
 
좌충우돌 ORM 개발기 2012 DAUM DEVON
좌충우돌 ORM 개발기 2012 DAUM DEVON좌충우돌 ORM 개발기 2012 DAUM DEVON
좌충우돌 ORM 개발기 2012 DAUM DEVONYounghan Kim
 
Direct x 12 초기화
Direct x 12 초기화Direct x 12 초기화
Direct x 12 초기화QooJuice
 

Ähnlich wie Effective c++ 정리 chapter 4 (20)

Effective c++ 1~8장
Effective c++ 1~8장 Effective c++ 1~8장
Effective c++ 1~8장
 
Chapter7~9 ppt
Chapter7~9 pptChapter7~9 ppt
Chapter7~9 ppt
 
Effective STL 1~4장 정리
Effective STL 1~4장 정리Effective STL 1~4장 정리
Effective STL 1~4장 정리
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshin
 
More effective c++ chapter1,2
More effective c++ chapter1,2More effective c++ chapter1,2
More effective c++ chapter1,2
 
Chapter5 ~ 6
Chapter5 ~ 6Chapter5 ~ 6
Chapter5 ~ 6
 
HolubOnPatterns/chapter2_2
HolubOnPatterns/chapter2_2HolubOnPatterns/chapter2_2
HolubOnPatterns/chapter2_2
 
9 object class
9 object class9 object class
9 object class
 
Effective c++(chapter 5,6)
Effective c++(chapter 5,6)Effective c++(chapter 5,6)
Effective c++(chapter 5,6)
 
Effective c++(chapter3,4)
Effective c++(chapter3,4)Effective c++(chapter3,4)
Effective c++(chapter3,4)
 
Effective java
Effective javaEffective java
Effective java
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
 
Mec chapter 5,6
Mec chapter 5,6Mec chapter 5,6
Mec chapter 5,6
 
Let's Go (golang)
Let's Go (golang)Let's Go (golang)
Let's Go (golang)
 
Effective c++ Chapter1,2
Effective c++ Chapter1,2Effective c++ Chapter1,2
Effective c++ Chapter1,2
 
Effective c++ chapter 7,8
Effective c++ chapter 7,8Effective c++ chapter 7,8
Effective c++ chapter 7,8
 
C++에서 Objective-C까지
C++에서 Objective-C까지C++에서 Objective-C까지
C++에서 Objective-C까지
 
Effective c++ chapter7_8_9_dcshin
Effective c++ chapter7_8_9_dcshinEffective c++ chapter7_8_9_dcshin
Effective c++ chapter7_8_9_dcshin
 
좌충우돌 ORM 개발기 2012 DAUM DEVON
좌충우돌 ORM 개발기 2012 DAUM DEVON좌충우돌 ORM 개발기 2012 DAUM DEVON
좌충우돌 ORM 개발기 2012 DAUM DEVON
 
Direct x 12 초기화
Direct x 12 초기화Direct x 12 초기화
Direct x 12 초기화
 

Effective c++ 정리 chapter 4

  • 1. EFFECTIVE C++ 정리 Chapter 4 설계 및 선언
  • 3. Item 18: 잘쓰기 쉽게 못쓰기 어렵게 • 인터페이스 • 유저와 코드가 접하는 경로 • 잘쓰기는 쉽게 못쓰기는 어렵게 만들자 • 유저가 생각하는 대로 동작하게 하자. • 잘못 쓴 경우 뭔가 항의의 몸부림을 치자. • 사용자가 실수하는 경우를 머리에 두고 있자.
  • 4. Item 18: 잘쓰기 쉽게 못쓰기 어렵게 • 날짜 클래스의 생성자 Date( int month, int day, int year); • 매개변수 순서를 잘못 입력한 경우 Data d(30, 3, 1995); //3, 30이 올바른 사용 • 가능한 범위를 넘어가는 경우 Data d(3, 40, 1995); //3월 40일은 없다.
  • 5. 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); //올바른 사용
  • 6. 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해서 안전하게
  • 7. Item 18: 잘쓰기 쉽게 못쓰기 어렵게 • 또 다른 제약 붙이기 const (item 3 참조) • operator*의 반환 타입을 const로 한정한 이유 • if( a * b = c )… //이런 실수를 막기 위해서 • 별다른 이유가 없다면 클래스는 기본 타입처럼 동작하게 하자 • 기존 int 역시 *연산에 다른 값을 대입할 수 없게 만들어져있음. • 유사한 쓰임새의 기본타입의 동작을 모방하자 • 일관성 있는 인터페이스의 제공
  • 8. Item 18: 잘쓰기 쉽게 못쓰기 어렵게 • STL에서 나타나는 비일관성의 예 • 데이터의 길이 또는 크기를 나타낼 떄 • Array는 length 프로퍼티로 • String은 length 메서드로 • List는 size 메서드로 • 비 일관성은 인터페이스를 해친다.
  • 9. Item 18: 잘쓰기 쉽게 못쓰기 어렵게 • 사용자 쪽에서 뭔가 외워야 하는 인터페이스는 잘못 되었다. • Investment* createInvestment(); //팩토리 함수 (in item 13) • 사용자 측에서 delete하는 것을 잊어서는 안 된다. • 사용자 측에서 별 고민 없이 그냥 사용할 수 있게 • std::shared_ptr<Investment> createInvestment(); //스마트포인터 반환 • 자동 해제되어 편하게 사용가능 • 사용자 실수를 원천 봉쇄 ( deleter도 설계자가 지정 가능 )
  • 10. Item 18: 잘쓰기 쉽게 못쓰기 어렵게 • 사용자 쪽에서 뭔가 외워야 하는 인터페이스는 잘못 되었다. • Investment* createInvestment(); //팩토리 함수 (in item 13) • 사용자 측에서 delete하는 것을 잊어서는 안 된다. • 사용자 측에서 별 고민 없이 그냥 사용할 수 있게 • std::shared_ptr<Investment> createInvestment(); //스마트포인터 반환 • 자동 해제되어 편하게 사용가능 • 사용자 실수를 원천 봉쇄 ( deleter도 설계자가 지정 가능 )
  • 11. Item 18: 잘쓰기 쉽게 못쓰기 어렵게 • 교차 DLL 문제 • 자원 할당된 포인터가 여기저기 돌아다니는 경우 • 객체 생성시에 특정 DLL에 있는 new를 사용 • 해제 시에는 다른 DLL에 있는 delete 를 사용 • tr1::std::shared_ptr은 그런 걱정 ㄴㄴ • Shared_ptr은 자신의 deleter를 control block에서 관리하기 때문 • 훌륭한 인터페이스의 대표적 사례 (http://ozt88.tistory.com/28 참조)
  • 13. Item 19: 클래스 설계는 타입을 설계하듯 • 클래스를 설계할 때, 새로운 언어의 타입을 설계하듯 • 자연스러운 구문, 의미체계의 구축 • 직관적이며 동시에 효율적인 기능 구현 • 고려사항들 1 • 객체 생성 소멸 방식 • 객체 초기화와 대입연산의 차이 • 값 복사의 의미와 구문
  • 14. Item 19: 클래스 설계는 타입을 설계하듯 • 고려사항들 2 • 타입의 domain 설정 • 값 제약 • Setter들에서 조건 체크 • 멤버 상속여부 • 상속 받았다면 어떤 멤버의 속성을 상속받아 사용할 것인가? • 상속해 줄 것이라면 어떤 멤버를 가상으로 설정할 것인가?
  • 15. Item 19: 클래스 설계는 타입을 설계하듯 • 고려사항들 3 • 타입 변환 • 어떤 타입들로 변환을 허락할 것인가 • 암시적 변환/ 명시적 변환 • 적당한 연산자와 멤버 함수 선정 • special function 중 어떤 것을 취사 선택하여 사용할 것인가? • Private로 봉인 가능 • 멤버함수 접근 권한 설정 (public/private/protected)
  • 16. Item 19: 클래스 설계는 타입을 설계하듯 • 고려사항들 4 • 선언되지 않은 인터페이스 • 보장할 자원, 성능, 예외 안정성 • 일반적 템플릿을 만들어야 하는가? • 정말로 필요한 타입인가? • 비슷한 클래스가 있고 기능이 몇 개 추가되지 않는다면 • 일반 비멤버 함수를 만들거나 템플릿을 몇 개 더 정의하는 게 낫다.
  • 18. Item 20: 객체 전달은 const T& • C++ 함수의 동작은 기본적으로 값에 의한 전달 • 일반 객체 paramete는 argumen에 대한 사본으로 생성 • 반환 값의 사본을 만들어 반환 객체를 받는다. • 예제 코드 Person class와 그를 상속받는 Student Class
  • 19. 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; };
  • 20. Item 20: 객체 전달은 const T& • Student를 인자로 전달받는 함수 validateStudent bool validataStudent(Student s); … Student plato; bool platoIsOK = validateStudent(plato); • 객체 전달에 필요한 비용 • Parameter의 Student 복사 생성자 호출 • Argument의 Student 소멸자 호출
  • 21. Item 20: 객체 전달은 const T& • 좀더 구체적인 비용 • Student 생성할 때 덩달아 불리는 생성자들 • 멤버변수 SchoolName 생성자, SchoolAddress 생성자 호출 • 상속받은 Person의 생성자 호출 • Person의 멤버변수 address, name 생성자 호출 • Student 해제할때 덩달아 호출되는 소멸자들 • 위에서 생성된 모든 객체들 각각 해제 • TOTAL : 생성자 6번, 소멸자 6번
  • 22. Item 20: 객체 전달은 const T& • 비효율적인 값 전달의 대체제 const T& • Reference-to-Const • 어차피 값으로 전달하면, 기존 객체의 값이 바뀔 일이 없다는 의미 • 상수성이 유지되면 사본을 만드는 대신 상수 객체의 참조를 넘기는게 이득 • No 생성 No 소멸 No 복사 • bool validateStudent( const Student& s ); • 복사손실 문제를 해결 • 정적 타입이 부모 클래스인 경우, 부모클래스의 복사 생성자 호출로 데이터 손실 • Window 클래스와 그것을 상속받은 WindowWIthScrollBars 클래스 예제
  • 23. Item 20: 객체 전달은 const T& • Class Window{ public: … std::string name() const; virtual void display() const; }; • Class WindowWithScrollBars :public Window{ public: … virtual void display() const; };
  • 24. Item 20: 객체 전달은 const T& • 윈도우의 이름을 출력하고 윈도우를 화면에 출력하는 함수 • void printNameAndDisplay(window w) • 여기에 WindowWithScrollBars 객체를 전달 • WindowWithScrollBars wwsb; printNameAndDisplay(wwsb); //복사 손실발생! • 여기에서도 해결책은 Reference-to-Const • void printNameAndDisplay(const Window& w); • 기존 매개변수의 참조만 사용하므로 데이터를 유지한 상태로 사용됨.
  • 25. Item 20: 객체 전달은 const T& • const T&가 만능은 아니다. • 참조자는 근본적으로는 포인터로 구현된다. • 전달 타입이 기본(primitive)타입인 경우 값 전달이 효율적이다. • STL의 반복자와 함수객체에서도 동일한 convention이 적용 • 반복자와 함수객체의 복사 생성자/할당자의 효율을 고려해야 한다. • 복사 손실 문제를 해결해야 한다.
  • 26. Item 20: 객체 전달은 const T& • 기본 타입이 아닌 값 전달의 위험성 (크기 작다고 값 복사 하지마) • 객체 크기가 작아도 복사 비용이 적은 것은 아님 • 포인터 타입의 깊은 복사의 경우 • 사용자 정의 타입은 연산 자체가 다르다. • Double 하나만 있는 객체와 Double을 다루는 방식이 다름 • 기본 double은 레지스터 하나로 처리, 하지만 객체는 다르다! • 포인터/참조는 무조건 레지스터 하나에 들어감 • 사용자 정의 타입의 크기는 변화가능하다.
  • 28. Item 21: 함수 반환에서 참조 반환을 피하자. • 참조 전달 만능주의에 빠지지 말자. • 반환형이 참조일 때, 이미 소멸된 객체의 참조를 반환할 가능성 • Rational 클래스에서 friend 함수 operator*의 예시 class Rational { … friend const Rational operator* (const Rational& lhs, const Rational& rhs); }
  • 29. Item 21: 함수 반환에서 참조 반환을 피하자. • 곱셈 결과를 값으로 반환하는 것이 정당한가? • 참조자를 반환하면 생성과 소멸에 드는 비용을 줄일 수 있다. • 참조자는 alias 즉 이미 존재하는 객체에 대한 참조 • 참조로 반환하는 경우 반환된 객체가 따로 어딘가에 존재해야 한다! • Rational을 참조로 반환하고 싶다면 • Operator*에서 Rational 객체를 따로 생성해야 한다. • 힙에 할당하거나, 스택에 생성하거나, 또는 static(^^)
  • 30. Item 21: 함수 반환에서 참조 반환을 피하자. • 스택에 생성하는 경우 const Rational& operator* (const Rational& lhs, const Rational& rhs) { Rational result(lhs.n * rhs.n, lhs.d * rhs.d); //반환값 스택에 생성 return result; } • 생성자 호출을 피하기위한 참조 반환이었지만 결국엔 생성해야한다. • 반환된 참조의 대상은 함수 종료 후 소멸된다. (댕글링 참조자)
  • 31. 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; } • 여전히 생성자 호출의 비용은 그대로 • 할당한 메모리 해제를 어떻게 할 것인가?
  • 32. Item 21: 함수 반환에서 참조 반환을 피하자. • 힙에 생성한 객체의 참조반환 • Rational w, x, y, z; w = x * y * z; //operator*(operator*(x, y), z); • 사용자 측에서 반환되는 참조자의 포인터에 접근할 방법이 없다. • 함수 내에서는 해제하면 안 된다. (댕글링 참조자 문제) • delete는 어디서 누가 어떻게 왜!!?
  • 33. 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
  • 34. Item 21: 함수 반환에서 참조 반환을 피하자. • 가능하면 그냥 값 전달하자. 물론 정석은 있다. inline const Rational operator* (const Rational& lhs, const Rational& rhs) {return Rational ( lhs.n * rhs.n, lhs.d * rhs.d );} • inline 키워드를 사용하여 불필요한 생성/소멸자 호출 방지 • 컴파일러 최적화에 맡기자.
  • 36. Item 22: 데이터 멤버들은 private에 선언하자 • Public 데이터 멤버 무엇이 문제인가? • 사용자의 문법적 일관성을 해친다. • 어떤 멤버는 데이터고 어떤 멤버는 함수다? • 사용자가 객체에 접근 가능한 방법이 모두 함수인 편이 일관성이 높다. • 사용자의 데이터 접근을 제어할 수 없다. • 데이터 멤버에 대해 자유로운 접근이 가능하다. • Private하면 직접 함수로 접근 방식을 커스터마이징 할 수 있다.
  • 37. Item 22: 데이터 멤버들은 private에 선언하자 • Public 데이터 멤버 무엇이 문제인가? 2 • 클래스의 온전한 캡슐화를 할 수 없다. • 클래스 캡슐화의 좋은 예 class SpeedDataCollection { public: void addValue(int speed); //새로운 데이터 추가. double averageSoFar() const; //평균 속도 반환 }
  • 38. Item 22: 데이터 멤버들은 private에 선언하자 • SpeedDataCollection 예시 • averageSoFar() 함수 어떻게 구현할 것인가? • 속도 평균값을 저장하는 데이터 멤버를 유지하고 그 값을 반환한다. • 성능 향성 but 객체 비대화 • 호출할 때마다 평균을 계산하여 반환한다. • 성능 구림 but 작은 사이즈 객체 • 상황에 따라 적합한 방식으로 구현해야 한다.
  • 39. Item 22: 데이터 멤버들은 private에 선언하자 • SpeedDataCollection 예시 • 평균값에 대한 캡슐화가 이루어지지 않았다면? • 구현방식이 바뀔 때마다 접근방식도 변경될 것이다. • 캡슐화를 통해서 외부 객체는 항상 동일한 방식으로 원하는 결과를 받을 수 있다. • 구현상의 융통성을 위한 캡슐화 • 함수를 통해 전달하기 때문에 데이터 전달 시 이벤트/동기화등 다양한 작업이 가능 • Public으로 선언된 멤버들을 변경하면 접근하는 방식을 손 봐야 한다는 의미 • 캡슐화는 클래스의 불변속성을 유지하는데 유리하다. • 유일한 통로를 통해 접근하므로, 함부로 변경되는 것을 막을 수 있다.
  • 40. Item 22: 데이터 멤버들은 private에 선언하자 • Protected의 데이터 멤버 무엇이 문제인가? • Public과 동일한 상황 • 데이터 멤버가 바뀌면 (제거되면) 깨지는 코드의 양과 캡슐화는 반비례 • 결국 protected인 데이터 멤버를 사용하는 모든 파생클래스들이 의존상태인 것. • 부모 클래스의 protected인 데이터 멤버가 변경되면 많은 코드가 변경돼야 한다. • 진짜 캡슐화를 하고 싶으면 데이터 멤버들은 private해라!
  • 42. Item 23: 비멤버 비프렌드 함수의 장점 • WebBrowser 클래스의 사례 • clearCache, History, Cookie를 한번에 하고 싶다면? • 함수 clearEverything(); • 멤버 함수로 할 것인가? • 비멤버 함수로 할 것인가? class WebBrowser { public: ... void clearCache(); void clearHistory(); void removeCookies(); };
  • 43. Item 23: 비멤버 비프렌드 함수의 장점 • 캡슐화의 측면 • 캡슐화는 데이터에 접근 가능한 루트가 적을 수록 증가한다. • 멤버함수는 객체의 private 데이터에 접근 가능하다. • 비멤버 + 비프랜드 함수는 private 데이터에 접근 불가능하다. • 캡슐화를 강화하는 것은 비멤버 + 비 프렌드 함수를 사용하는 것! • 다른 비 프랜드 객체의 멤버함수를 사용하는 것도 훌륭한 선택이다.
  • 44. Item 23: 비멤버 비프렌드 함수의 장점 • 비 멤버 함수와 해당 객체를 같은 네임스페이스 안에 두자 • Namespace WebBrowserStuff { class WebBrowser {…}; void clearBrowser (WebBrowser& wb); } • Utility 함수를 다루는 매우 훌륭한 방법 • 같은 네임스페이스를 여러 개의 소스로 나누어서 사용하는 것이 가능하다. • 다양한 utility 기능들을 기능별로 헤더로 분리하여 선언하는 것이 가능
  • 45. Item 23: 비멤버 비프렌드 함수의 장점 • //in WebBrowser.h Namespace WebBrowserStuff { class WebBrowser { … }; } //in WebBrowserBookmarks.h Namespace WebBrowserStuff { … } • Std 표준 라이브러리도 이런 형식으로 구성된다. • 필요한 기능만 include 해서 사용할 수 있도록 • 실제로 사용하는 요소만 컴파 일 의존성을 고려하면 된다. • 패키징 유연성!
  • 46. Item 23: 비멤버 비프렌드 함수의 장점 • Utility 함수 집합의 확장 • 해당 네임스페이스에 비멤버 비프랜드 함수를 추가하면 됨 • 새로운 헤더 만들고 네임스페이스에 원하는 함수들 추가하면 끝 • 캡슐화가 잘되고 확장도 쉬운 비멤버 비프렌드 함수 씁시다.
  • 48. Item 24: 멤버함수의 Blind Sight • 다시 Rational class (지겹다) • 곱셈 연산을 지원하고 싶다. • 멤버함수 / 비멤버 함수/ 비멤버 프렌드 함수 (선택지가 너무 많아) • 객체 지향 파워로 돌파해보자. • 곱셈 연산은 Rational 클래스 자체랑 관련이 있으니까 • 멤버함수로 넣어주는 게 적절할 것만 같아. • Item 23에서는 캡슐화가 어쩌고 했지만 넘어가도록 하자
  • 49. 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; • 자~알 돌아간다.
  • 50. 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; • 자~알 돌아간다.
  • 51. Item 24: 멤버함수의 Blind Sight • 혼합형 수치 연산하고 싶다. 헠헠 Int * Rational 같은거… result = oneHalf * 2; //OK result = 2 * oneHalf //에러?! 왜?! • 문제의 원인 result = oneHalf. operator*(2); //Rational에서 메소드 호출 result = 2. operator*(oneHalf); //Int에서 메소드 호출??
  • 52. Item 24: 멤버함수의 Blind Sight • 다른 방법의 돌파구 result = operator*(2, oneHalf); //에러!? • Rational::operator*는 좌변에 반드시 this를 사용하기 때문! • 우변은 암시적 형변환으로 자연스러운 처리 Rational temp(2); result = oneHalf.operator*( temp ); • 생성자가 explicit였으면 형변환 불가능으로 이마저도 에러
  • 53. Item 24: 멤버함수의 Blind Sight • 양변에 모두 암시적 형변환을 지원하고 싶다면? • 멤버 함수로는 안된다. • 비멤버 함수로 처리 const Rational operator*(const Rational& lhs, const Rational& rhs); {//좌변과 우변을 모두 설정가능 return Rational(lhs.numerator() * rhs.numerator(), //캡슐화된 접근 lhs.denominator() * rhs.denominator()); }
  • 54. Item 24: 멤버함수의 Blind Sight • 완벽하게 캡슐화된 접근방식으로 operator*를 구현가능하다. • 쓸데없이 friend 선언해서 캡슐화를 줄이지 말자. • 객체를 다루는 함수는 가능하면 비 friend 함수로 캡슐화된 접근을 통 해 문제를 해결하자.
  • 56. Item 25: 예외 처리 없는 swap • Swap의 중요성 • 예외 안전성 프로그래밍에 반드시 필요한 역할 수행 • 자기 대입 현상에 대처하는데 핵심적 역할 (item 11참조) • STL에 추가됨 • 잘 동작하는 swap을 만들어 두자.
  • 57. Item 25: 예외 처리 없는 swap • 표준의 std::swap • 전형적인 구현 • T가 복사연산만 지원한다면 제 대로 동작한다. • 세 번의 복사 연산 비용 • 썩 훌륭해 보이지 않는다. • Template<typename T> void swap(T& a, T& b) { T temp(a); a = b; b = temp; }
  • 58. Item 25: 예외 처리 없는 swap • Pimple Idiom 사용하는 객체의 swap • Pimple Idiom은 (http://ozt88.tistory.com/32 참조) • Swap에서 Impl*의 포인터 pImpl의 주소값만 맞바꿔주면 충분하다. • 하지만 std::swap의 코드대로라면 깊은 복사를 3번 수행한다. • 초 비효율
  • 59. Item 25: 예외 처리 없는 swap • std::swap에 템플릿 특수화를 더하여 해결하자 • Pimple Idiom을 사용하는 객체 Widget에 swap() 메소드 준비 • template<> void swap<Widget>(Widget& a, Widget& b){ a.swap(b); }
  • 60. Item 25: 예외 처리 없는 swap • 좀더 확장해보자. • Pimpl에 들어가는 데이터 타입을 매개변수로 받는 클래스 템플릿 • Widget<T> template<typename T> void swap<Widget<T>>(Widget<T>& a, Widget<T>& b) { a. swap( b ); } //에러??!! • 함수 템플릿에서 부분 특수화를 허용하지 않는다. • 클래스 템플릿은 허용한다.
  • 61. Item 25: 예외 처리 없는 swap • 부분 특수화 대신 swap을 오버로딩 하는 방법으로 해결가능 template<typename T> void swap(Widget<T>& a, Widget<T>& b) { a.swap(b); } //템플릿 특수화를 버리고 일반 오버로딩을 사용 • 하지만 이 코드는 제대로 동작하지 않는다. • std에 새로운 함수를 추가하는 것이 불가능하기 때문 • std 밖에서도 쉽게 사용할 수 있는 방법은?
  • 62. 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); … }
  • 63. 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); }
  • 64. Item 25: 예외 처리 없는 swap • 요점정리 • Swap 효율이 나쁘지 않으면 그냥 써라 • 따로 Swap을 만들어 쓰고싶다면 • 해당 타입의 public 멤버함수로 원하는 swap을 정의해라 • 클래스 또는 템플릿이 있는 네임스페이스에 멤버함수 swap을 호출하는 비멤버 swap을 만들어 넣어라. • 일반 클래스에대한 특수화는 std::swap에서 템플릿 특수화하는 것으로 충분하다. • swap 호출전에 using std::swap;을 선언하자.