Effective C++
Chapter 3 그리고 4
‘자원관리’ 그리고 ‘설계 및 선언’
131039 신동찬
Resource(자원)
석유, 석탄, 천연가스…
미네랄, 가스…
상대방 전화 번호, 차, 돈…
프로그래밍 분야에서 자원은??
운영체제 관련 서적에서 배우시고…
Chapter 3에서는
‘메모리 관리’
로 시작해서
‘메모리 관리’
로 끝난다
그만큼 중요한 이슈라는 의미!
흔히 책상을 메모리에 빗대는 경우가 많은데
아래 2 시스템의 메모리(책상)를 보자
뭐가 다른가?
올려진 책을 한 번에 모두 공부하면
저 사람은 ‘신'이라 할 수 있다…
(이건 신성모독인가…)
Void f()
{
Investment *pInv = createInvestment();
…
delete pInv;
}
객체를 생성하는 녀석이 호출자(caller)면
객체를 지워야 하는 쪽도 호출자이다
따라서, 코드 당연히 잘 돌겠네~
확실합니까?
중간에 함수를 빠져나갈 구멍이 있다면
파이프 깨지는 것은 일도 아니다
구멍 뚫기 전문가
• Return
• Continue
• Goto
• 예외처리(exception)
함수 중간에 이 친구들 만나면 빵빵 터진다
구멍이 생기기 전에
막을 방법은 없는가?
구멍 막기 전문가
• std::auto_ptr
• tr1::shared_ptr
소멸자에서 자신이 가리키는 대상에
자동으로 delete를 먹이는 친구들
주요 관점!
1. 자원을 획득한 후에 자원 관리 객체에게 전달(자원 획득 즉 초기화, RAII)
2. 자원 관리 객체는 자신의 소멸자에서 자원이 해제되는 것을 보장
‘구멍 막기 전문가’==자원 관리 객체!
알아서 지워 주기 때문에
Smart Pointer라는 명칭 사용
그런데…
알아서 잘하면 하나로 충분하지 않나요?
왜 둘이나 되죠?
std에서 지원하는 std::auto_ptr에 제약사항이 많아
Boost에서 기능을 추가해 내놓은 것이 td1::shared_ptr
std::auto_ptr 요구사항
• 대입 연산자 사용은 2중 delete 가능성이 있어
기존 smart pointer가 null로 변경
• 한 객체를 둘 이상의 smart pointer가 물면 안 됨
td1::auto_ptr 기능
• 어떤 자원을 가리키는 횟수를 확인 할 수 있음
• 해당 자원을 아무도 가리키지 않는 경우 제거
자동으로 하니까 편하네!
이제 끝?
‘아니다’
여전히
‘인간의 실수’가 남았다
std::auto_ptr<std::string> aps(new std::string[10]);
std::tr1::shard_ptr<int> spi(new int[1024]);
스마트한 녀석들이니깐
알아서 잘 생성하고 지우겠지?!
아니다!
스마트한 녀석도 불러줘야 일을 하지…
생성을 몇 개 했는가?
삭제는 몇 개 했는가?
배열처럼 동시에 여러 생성자가 호출 되면
‘소멸자가 호출되는 횟수’가 중요하다
하나의 객체와 여러 개의 객체는
생긴 것부터 다르다
즉,
new와 delete는 짝을 맞춰야 한다
스마트한 녀석들이라도 반드시!
std::auto_ptr<std::string> aps(new std::string[10]);
std::tr1::shard_ptr<int> spi(new int[1024]);
delete [] aps;
delete [] spi;
[] 를 표시해 최초 n개의 값을 받아
delete에게 포인터가 배열을 가리킴을 알림
그런데…
스마트한 녀석들도 사고 치지 않을까?
날 믿어! 라고 외치던 라이브러리에
뒤통수 맞아 봐야 정신 차리지?
그래서! 준비 했습니다
외부에서 자원 관리 클래스 파고 들기
//daysHeld 전방 선언
int daysHels(const Investment *pi);
…
std::tr1::shared_ptr<Investment> pInv( createInvestment() );
int days = daysHeld(pInv);
현재 전달인자의 타입은 무엇인가?
1. Investment*
2. tr1::shared_ptr<investment>
잊으셨나본데…
스마트한 녀석들 == ‘자원 관리 객체‘
당신이 생각하는 그 객체가 아니다
당근 착하지도 않다!
알아서 바꿔주는 것도 하지 않는다
원래 똑똑한 놈들이 더 악랄하지 않던가
별도의 접근 방법이 필요하다
실제 객체에 접근하기 위한 방법
1. 자원 관리 객체가 제공하는 get() 메소드를 사용
2. 자원 관리 객체에 operator ->, operator *를 써서 직접 접근
3. 클래스에 명시적 변환 함수로 get 제공
4. 암시적 변환 함수로 접근
명시적 변환은 안전성 확보
암시적 변환은 고객 편의성
각각 장점과 단점이 존재함
지금까지 힙 기반 자원에 대해 확인했는데
항상 힙에만 자원이 생길까?
출처 -http://donghwada.tistory.com/93
아니다!
힙이 아니면 스마트도 소용 없음
자원 관리 클래스를 직접 만들어야…
class Lock{ //mutex 자원 획득/해제 클래스
public:
explicit Lock(Mutex *pm)
: mutexPtr(pm)
{ lock(mutexPtr); }
~Lock() { unlock(mutexPtr); }
private:
Mutex *mutexPtr;
};
Mutex m;
…
{
Lock ml1 (&m);
Lock ml2 (ml1); //얘 어째야 할까요?
}
만약 없었다면 임계영역(블록 영역)을 벗어나며
자동으로 해제 되어 잘 구동 되었을 것임
모든 방법을 다 고려해보자!
• 복사를 못하게 방지한다
• 관리하고 있는 자원에 대해 참조 카운팅 수행
• 관리하고 있는 자원을 진짜로 복사(deep copy)
• 관리하는 자원의 소유권 이전
Be OK!
하고 나서 이후 조치를 알아서 해줘야 함
복사 금지/ 참조 카운팅 수행 추천
또 항상 문제가 되는 실행순서에 집중하자!
new로 생성한 객체를 스마트 포인터에 저장하는 도중에…
생성하기도 전에 객체를 스마트 포인터에 밀어 넣으면?
터진다!
컴파일러 제작사가 프로그램을 좌지우지
호출 순서에 의해 문제 발생
processWidget( std::tr1::shared_ptr<Widget>( new Widget ), priority() );
원하는 실행 순서
1. priority를 호출
2. new Widget 실행
3. tr1::shared_ptr 생성자 호출
4. 생성한 Widget 감싸기
어떤 회사 컴파일러 실행 순서
1. new Widget 실행
2. priority 호출
3. tr1::shared_ptr 생성자 호출
4. 생성한 Widget 감싸기
priority 호출에서 문제가 발생하면?
생성한 Widget은 ‘낙동강 오리알’
processWidget( std::tr1::shared_ptr<Widget>( new Widget ), priority() );
std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget( pw, priority() );
한 줄로 스스로를 구원하라
OOP
결국 설계는 객체 지향
객체화를 얼마나 잘했는지가 중요함
정확성, 효율
캡슐화, 유지보수성, 확장성
규약 준수 등
‘제대로 쓰기에 쉽고 엉터리로 쓰기에 어려운‘
인터페이스를 만들어라
각 객체를 연결하는 인터페이스를 관리 하는 자가
설계(혹은 사용자)를 지배한다
• 범위를 벗어난 값이 들어오면 오류
• 잘못된 타입이 들어오면 오류
• 일관성 있는 인터페이스 만들기
• 사용자가 실수할 가능성이 있는 부분을 제거
(미리 auto_ptr 또는 tr1::shared_ptr로 감싸기)
‘좋은’타입 설계하기
클래스도 결국 내부가 복잡한 타입
좋은 타입을 설계하면 모든 것이 해결
효과적인 타입 설계의 조건
• 문법이 자연스럽고
• 의미구조가 직관적이며
• 효율적인 구현
어디서 많이 보던 장면
출처 –질풍기획 시즌2
새로 정의한 타입의 객체 생성 및 소멸은 어떻게 이뤄져야 하는가?
객체 초기화는 객체 대입과 어떻게 달라야 하는가?
…
새로 만드는 타입이 얼마나 일반적인가?
정말로 꼭 필요한 타입인가?
질답이 책 내용 대부분입니다
공부합시다
전체 그림은 그렇다치고
각 객체간 전달 방식은 어떻게 해야 될까요?
1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’
2. 함수 리턴값을 참조자로 선택하지 말자
둘 하는 이야기가 반대 아닌가요?
1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’
2. 함수 리턴값을 참조자로 선택하지 말자
bool validateStudent( student s);
bool validateStudent( const student& s);
두 함수를 실행하면 어떤 차이가 발생하는가?
class person {
public:
person();
virtual ~Person();
private:
std::string name;
};
class student: public person {
public:
student();
!student();
private:
std::string schoolName;
};
1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’
2. 함수 리턴값을 참조자로 선택하지 말자
bool validateStudent( student s);
• student 복사 생성자, 소멸자 호출
• student string 객체 생성, 소멸
• person 복사 생성자, 소멸자 호출
• person string 객체 생성, 소멸
• 총 비용 생성자 6회, 소멸자 6회
bool validateStudent( const student& s);
• 참조형이라 이전에 생성 된 것을 사용
• 복사손실 문제도 사라짐
(복사손실 문제 – 자식 클래스가 부모클래스로 형변환
등을 거치며 정상 구동하지 않는 것)
이외에도
지속적인 비용문제, 레지스터 입력 등
효율성 측면에서 반드시 필요
1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’
2. 함수 리턴값을 참조자로 선택하지 말자
주의!
기본제공 타입, STL 반복자, 함수 객체 타입
위 상황에서는 ‘값에 의한 전달’이 적절
1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’
2. 함수 리턴값을 참조자로 선택하지 말자
엄청난 효율!
이것을 정녕 포기하란 말입니까?
1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’
2. 함수 리턴값을 참조자로 선택하지 말자
스택 영역 특징
• 함수가 시작 될 때 같이 늘어남
• 함수가 종료 될 때 같이 소멸됨
값을 함수에 넘겨주면
return 객체를 함수에서 생성해야 함
함수 내부에서 생성하는 것은 스택에 들어감
return 객체도 스택에 들어감
함수가 종료되며 스택 영역 소멸
return 객체도 소멸?!
이미 당신은 없는 값에 접근중!
캡슐화(Encapsulation)
각 객체는 최대한 서로 배제 되어야 함
객체 인터페이스를 통일
특히, 함수로 통일해야 함
동시에 모든 데이터 멤버를 숨긴다
서로의 의존성을 줄이기 위해!
따라서!
모든 데이터 멤버(멤버 변수)는 private 영역
클래스 제작자는 문법적으로 일관성 유지
세밀한 접근 제어 가능
내부 구현의 융통성 발휘
protected도 있잖아요?는 훼이크닷
자식 클래스에서 접근하는 것도
문법적 일관성과 세밀한 접근 측면에서
public과 동일
같은 맥락에서
비멤버 비프렌드 함수를 선호하라
패키징 유연성이 높아지며
컴파일 의존도를 낮출 수 있음
비멤버 함수인 경우 자체 확장성도 확보
프렌드는 ‘일진’프렌드다
선언하는 객체는 빵셔틀일뿐…
(친구 아이다 아이가)
프렌드 선언하면 내부를 탈탈 털어줌
private까지 싸그리 털림
간편함에 속지 말자
비멤버 함수는 멀티 플레이어
타입 변환이 모든 매개변수에 적용하는 경우
비멤버 함수를 선언
비멤버 함수가 아닌 경우
• 인자 배치 순서에 따라서 실행 일관성이 깨짐
• 혼합형 수치 연산에서도 실행 일관성이 깨짐
SWAP 함수 효율화 하기
swap은 일상적이지만 입력 객체에 따라 성능이 휘청임
부모, 자식 객체등을 반복 복사
표준 swap, 멤버 swap, 비멤버 swap, 특수화한 std::swap
각 상황별로 나눠 사용할 수 있도록 함
• 일단 표준형에서 효율이 좋으면 현상태 유지
• 그러나 효율이 좋지 않다면
1. 객체 내부에 swap 함수를 제작
2. 해당 클래스에 비멤버 swap을 제작해 넣음
3. 객체 내부 swap 함수를 비멤버 함수가 호출하도록 함
4. 새로운 클래스에 대한 std::swap 특수화 버전 준비
5. 특수화 버전에서도 객체 내부 swap 함수 호출
6. 사용자가 swap 호출시 호출함수에 namespace 사용을 선언
주의!
다 좋은데 std에 뭔가를 새로 추가하려 하지 말 것
std에 추가는 순간 실행되는 결과가 미정의 됨
chapter 3, 4 끝

Effective c++ chapter3, 4 요약본

  • 1.
    Effective C++ Chapter 3그리고 4 ‘자원관리’ 그리고 ‘설계 및 선언’ 131039 신동찬
  • 2.
    Resource(자원) 석유, 석탄, 천연가스… 미네랄,가스… 상대방 전화 번호, 차, 돈… 프로그래밍 분야에서 자원은?? 운영체제 관련 서적에서 배우시고…
  • 3.
    Chapter 3에서는 ‘메모리 관리’ 로시작해서 ‘메모리 관리’ 로 끝난다 그만큼 중요한 이슈라는 의미!
  • 4.
    흔히 책상을 메모리에빗대는 경우가 많은데 아래 2 시스템의 메모리(책상)를 보자 뭐가 다른가? 올려진 책을 한 번에 모두 공부하면 저 사람은 ‘신'이라 할 수 있다… (이건 신성모독인가…)
  • 5.
    Void f() { Investment *pInv= createInvestment(); … delete pInv; } 객체를 생성하는 녀석이 호출자(caller)면 객체를 지워야 하는 쪽도 호출자이다 따라서, 코드 당연히 잘 돌겠네~ 확실합니까?
  • 6.
    중간에 함수를 빠져나갈구멍이 있다면 파이프 깨지는 것은 일도 아니다 구멍 뚫기 전문가 • Return • Continue • Goto • 예외처리(exception) 함수 중간에 이 친구들 만나면 빵빵 터진다
  • 7.
    구멍이 생기기 전에 막을방법은 없는가? 구멍 막기 전문가 • std::auto_ptr • tr1::shared_ptr 소멸자에서 자신이 가리키는 대상에 자동으로 delete를 먹이는 친구들
  • 8.
    주요 관점! 1. 자원을획득한 후에 자원 관리 객체에게 전달(자원 획득 즉 초기화, RAII) 2. 자원 관리 객체는 자신의 소멸자에서 자원이 해제되는 것을 보장 ‘구멍 막기 전문가’==자원 관리 객체! 알아서 지워 주기 때문에 Smart Pointer라는 명칭 사용
  • 9.
    그런데… 알아서 잘하면 하나로충분하지 않나요? 왜 둘이나 되죠? std에서 지원하는 std::auto_ptr에 제약사항이 많아 Boost에서 기능을 추가해 내놓은 것이 td1::shared_ptr std::auto_ptr 요구사항 • 대입 연산자 사용은 2중 delete 가능성이 있어 기존 smart pointer가 null로 변경 • 한 객체를 둘 이상의 smart pointer가 물면 안 됨 td1::auto_ptr 기능 • 어떤 자원을 가리키는 횟수를 확인 할 수 있음 • 해당 자원을 아무도 가리키지 않는 경우 제거
  • 10.
    자동으로 하니까 편하네! 이제끝? ‘아니다’ 여전히 ‘인간의 실수’가 남았다
  • 11.
    std::auto_ptr<std::string> aps(new std::string[10]); std::tr1::shard_ptr<int>spi(new int[1024]); 스마트한 녀석들이니깐 알아서 잘 생성하고 지우겠지?! 아니다! 스마트한 녀석도 불러줘야 일을 하지… 생성을 몇 개 했는가? 삭제는 몇 개 했는가?
  • 12.
    배열처럼 동시에 여러생성자가 호출 되면 ‘소멸자가 호출되는 횟수’가 중요하다 하나의 객체와 여러 개의 객체는 생긴 것부터 다르다 즉, new와 delete는 짝을 맞춰야 한다 스마트한 녀석들이라도 반드시!
  • 13.
    std::auto_ptr<std::string> aps(new std::string[10]); std::tr1::shard_ptr<int>spi(new int[1024]); delete [] aps; delete [] spi; [] 를 표시해 최초 n개의 값을 받아 delete에게 포인터가 배열을 가리킴을 알림
  • 14.
    그런데… 스마트한 녀석들도 사고치지 않을까? 날 믿어! 라고 외치던 라이브러리에 뒤통수 맞아 봐야 정신 차리지? 그래서! 준비 했습니다 외부에서 자원 관리 클래스 파고 들기
  • 15.
    //daysHeld 전방 선언 intdaysHels(const Investment *pi); … std::tr1::shared_ptr<Investment> pInv( createInvestment() ); int days = daysHeld(pInv); 현재 전달인자의 타입은 무엇인가? 1. Investment* 2. tr1::shared_ptr<investment>
  • 16.
    잊으셨나본데… 스마트한 녀석들 ==‘자원 관리 객체‘ 당신이 생각하는 그 객체가 아니다 당근 착하지도 않다! 알아서 바꿔주는 것도 하지 않는다 원래 똑똑한 놈들이 더 악랄하지 않던가 별도의 접근 방법이 필요하다
  • 17.
    실제 객체에 접근하기위한 방법 1. 자원 관리 객체가 제공하는 get() 메소드를 사용 2. 자원 관리 객체에 operator ->, operator *를 써서 직접 접근 3. 클래스에 명시적 변환 함수로 get 제공 4. 암시적 변환 함수로 접근 명시적 변환은 안전성 확보 암시적 변환은 고객 편의성 각각 장점과 단점이 존재함
  • 18.
    지금까지 힙 기반자원에 대해 확인했는데 항상 힙에만 자원이 생길까? 출처 -http://donghwada.tistory.com/93 아니다! 힙이 아니면 스마트도 소용 없음 자원 관리 클래스를 직접 만들어야…
  • 19.
    class Lock{ //mutex자원 획득/해제 클래스 public: explicit Lock(Mutex *pm) : mutexPtr(pm) { lock(mutexPtr); } ~Lock() { unlock(mutexPtr); } private: Mutex *mutexPtr; }; Mutex m; … { Lock ml1 (&m); Lock ml2 (ml1); //얘 어째야 할까요? } 만약 없었다면 임계영역(블록 영역)을 벗어나며 자동으로 해제 되어 잘 구동 되었을 것임
  • 20.
    모든 방법을 다고려해보자! • 복사를 못하게 방지한다 • 관리하고 있는 자원에 대해 참조 카운팅 수행 • 관리하고 있는 자원을 진짜로 복사(deep copy) • 관리하는 자원의 소유권 이전 Be OK! 하고 나서 이후 조치를 알아서 해줘야 함 복사 금지/ 참조 카운팅 수행 추천
  • 21.
    또 항상 문제가되는 실행순서에 집중하자! new로 생성한 객체를 스마트 포인터에 저장하는 도중에… 생성하기도 전에 객체를 스마트 포인터에 밀어 넣으면? 터진다! 컴파일러 제작사가 프로그램을 좌지우지 호출 순서에 의해 문제 발생
  • 22.
    processWidget( std::tr1::shared_ptr<Widget>( newWidget ), priority() ); 원하는 실행 순서 1. priority를 호출 2. new Widget 실행 3. tr1::shared_ptr 생성자 호출 4. 생성한 Widget 감싸기 어떤 회사 컴파일러 실행 순서 1. new Widget 실행 2. priority 호출 3. tr1::shared_ptr 생성자 호출 4. 생성한 Widget 감싸기 priority 호출에서 문제가 발생하면? 생성한 Widget은 ‘낙동강 오리알’
  • 23.
    processWidget( std::tr1::shared_ptr<Widget>( newWidget ), priority() ); std::tr1::shared_ptr<Widget> pw(new Widget); processWidget( pw, priority() ); 한 줄로 스스로를 구원하라
  • 24.
    OOP 결국 설계는 객체지향 객체화를 얼마나 잘했는지가 중요함 정확성, 효율 캡슐화, 유지보수성, 확장성 규약 준수 등
  • 25.
    ‘제대로 쓰기에 쉽고엉터리로 쓰기에 어려운‘ 인터페이스를 만들어라 각 객체를 연결하는 인터페이스를 관리 하는 자가 설계(혹은 사용자)를 지배한다 • 범위를 벗어난 값이 들어오면 오류 • 잘못된 타입이 들어오면 오류 • 일관성 있는 인터페이스 만들기 • 사용자가 실수할 가능성이 있는 부분을 제거 (미리 auto_ptr 또는 tr1::shared_ptr로 감싸기)
  • 26.
    ‘좋은’타입 설계하기 클래스도 결국내부가 복잡한 타입 좋은 타입을 설계하면 모든 것이 해결 효과적인 타입 설계의 조건 • 문법이 자연스럽고 • 의미구조가 직관적이며 • 효율적인 구현
  • 27.
    어디서 많이 보던장면 출처 –질풍기획 시즌2
  • 28.
    새로 정의한 타입의객체 생성 및 소멸은 어떻게 이뤄져야 하는가? 객체 초기화는 객체 대입과 어떻게 달라야 하는가? … 새로 만드는 타입이 얼마나 일반적인가? 정말로 꼭 필요한 타입인가? 질답이 책 내용 대부분입니다 공부합시다
  • 29.
    전체 그림은 그렇다치고 각객체간 전달 방식은 어떻게 해야 될까요? 1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’ 2. 함수 리턴값을 참조자로 선택하지 말자 둘 하는 이야기가 반대 아닌가요?
  • 30.
    1. ‘값에 의한전달’ 보다는 ‘상수 객체 참조자에 의한 전달’ 2. 함수 리턴값을 참조자로 선택하지 말자 bool validateStudent( student s); bool validateStudent( const student& s); 두 함수를 실행하면 어떤 차이가 발생하는가? class person { public: person(); virtual ~Person(); private: std::string name; }; class student: public person { public: student(); !student(); private: std::string schoolName; };
  • 31.
    1. ‘값에 의한전달’ 보다는 ‘상수 객체 참조자에 의한 전달’ 2. 함수 리턴값을 참조자로 선택하지 말자 bool validateStudent( student s); • student 복사 생성자, 소멸자 호출 • student string 객체 생성, 소멸 • person 복사 생성자, 소멸자 호출 • person string 객체 생성, 소멸 • 총 비용 생성자 6회, 소멸자 6회 bool validateStudent( const student& s); • 참조형이라 이전에 생성 된 것을 사용 • 복사손실 문제도 사라짐 (복사손실 문제 – 자식 클래스가 부모클래스로 형변환 등을 거치며 정상 구동하지 않는 것) 이외에도 지속적인 비용문제, 레지스터 입력 등 효율성 측면에서 반드시 필요
  • 32.
    1. ‘값에 의한전달’ 보다는 ‘상수 객체 참조자에 의한 전달’ 2. 함수 리턴값을 참조자로 선택하지 말자 주의! 기본제공 타입, STL 반복자, 함수 객체 타입 위 상황에서는 ‘값에 의한 전달’이 적절
  • 33.
    1. ‘값에 의한전달’ 보다는 ‘상수 객체 참조자에 의한 전달’ 2. 함수 리턴값을 참조자로 선택하지 말자 엄청난 효율! 이것을 정녕 포기하란 말입니까?
  • 34.
    1. ‘값에 의한전달’ 보다는 ‘상수 객체 참조자에 의한 전달’ 2. 함수 리턴값을 참조자로 선택하지 말자 스택 영역 특징 • 함수가 시작 될 때 같이 늘어남 • 함수가 종료 될 때 같이 소멸됨 값을 함수에 넘겨주면 return 객체를 함수에서 생성해야 함 함수 내부에서 생성하는 것은 스택에 들어감 return 객체도 스택에 들어감 함수가 종료되며 스택 영역 소멸 return 객체도 소멸?! 이미 당신은 없는 값에 접근중!
  • 35.
    캡슐화(Encapsulation) 각 객체는 최대한서로 배제 되어야 함 객체 인터페이스를 통일 특히, 함수로 통일해야 함 동시에 모든 데이터 멤버를 숨긴다 서로의 의존성을 줄이기 위해!
  • 36.
    따라서! 모든 데이터 멤버(멤버변수)는 private 영역 클래스 제작자는 문법적으로 일관성 유지 세밀한 접근 제어 가능 내부 구현의 융통성 발휘 protected도 있잖아요?는 훼이크닷 자식 클래스에서 접근하는 것도 문법적 일관성과 세밀한 접근 측면에서 public과 동일
  • 37.
    같은 맥락에서 비멤버 비프렌드함수를 선호하라 패키징 유연성이 높아지며 컴파일 의존도를 낮출 수 있음 비멤버 함수인 경우 자체 확장성도 확보 프렌드는 ‘일진’프렌드다 선언하는 객체는 빵셔틀일뿐… (친구 아이다 아이가) 프렌드 선언하면 내부를 탈탈 털어줌 private까지 싸그리 털림 간편함에 속지 말자
  • 38.
    비멤버 함수는 멀티플레이어 타입 변환이 모든 매개변수에 적용하는 경우 비멤버 함수를 선언 비멤버 함수가 아닌 경우 • 인자 배치 순서에 따라서 실행 일관성이 깨짐 • 혼합형 수치 연산에서도 실행 일관성이 깨짐
  • 39.
    SWAP 함수 효율화하기 swap은 일상적이지만 입력 객체에 따라 성능이 휘청임 부모, 자식 객체등을 반복 복사 표준 swap, 멤버 swap, 비멤버 swap, 특수화한 std::swap 각 상황별로 나눠 사용할 수 있도록 함 • 일단 표준형에서 효율이 좋으면 현상태 유지 • 그러나 효율이 좋지 않다면 1. 객체 내부에 swap 함수를 제작 2. 해당 클래스에 비멤버 swap을 제작해 넣음 3. 객체 내부 swap 함수를 비멤버 함수가 호출하도록 함 4. 새로운 클래스에 대한 std::swap 특수화 버전 준비 5. 특수화 버전에서도 객체 내부 swap 함수 호출 6. 사용자가 swap 호출시 호출함수에 namespace 사용을 선언
  • 40.
    주의! 다 좋은데 std에뭔가를 새로 추가하려 하지 말 것 std에 추가는 순간 실행되는 결과가 미정의 됨
  • 41.