41
Effective C++ Chapter 3 그리고 4 ‘자원관리’ 그리고 ‘설계 및 선언’ 131039 신동찬

Effective c++ chapter3, 4 요약본

Embed Size (px)

DESCRIPTION

NHN NEXT 2014년 1학기 C++ Advanced 과제 수행 산출물 C++로 메모리 관리하는 기법 C++로 클래스 설계하는 기법(OOP)

Citation preview

Page 1: Effective c++ chapter3, 4 요약본

Effective C++ Chapter 3 그리고 4‘자원관리’ 그리고 ‘설계 및 선언’

131039 신동찬

Page 2: Effective c++ chapter3, 4 요약본

Resource(자원)석유, 석탄, 천연가스…미네랄, 가스…상대방 전화 번호, 차, 돈…

프로그래밍 분야에서 자원은??

운영체제 관련 서적에서 배우시고…

Page 3: Effective c++ chapter3, 4 요약본

Chapter 3에서는

‘메모리 관리’로 시작해서

‘메모리 관리’로 끝난다

그만큼 중요한 이슈라는 의미!

Page 4: Effective c++ chapter3, 4 요약본

흔히 책상을 메모리에 빗대는 경우가 많은데아래 2 시스템의 메모리(책상)를 보자뭐가 다른가?

올려진 책을 한 번에 모두 공부하면

저 사람은 ‘신'이라 할 수 있다…(이건 신성모독인가…)

Page 5: Effective c++ chapter3, 4 요약본

Void f(){

Investment *pInv = createInvestment();…delete pInv;

}

객체를 생성하는 녀석이 호출자(caller)면객체를 지워야 하는 쪽도 호출자이다

따라서, 코드 당연히 잘 돌겠네~

확실합니까?

Page 6: Effective c++ chapter3, 4 요약본

중간에 함수를 빠져나갈 구멍이 있다면파이프 깨지는 것은 일도 아니다

구멍 뚫기 전문가

• Return• Continue• Goto• 예외처리(exception)

함수 중간에 이 친구들 만나면 빵빵 터진다

Page 7: Effective c++ chapter3, 4 요약본

구멍이 생기기 전에막을 방법은 없는가?

구멍 막기 전문가

• std::auto_ptr• tr1::shared_ptr

소멸자에서 자신이 가리키는 대상에자동으로 delete를 먹이는 친구들

Page 8: Effective c++ chapter3, 4 요약본

주요 관점!

1. 자원을 획득한 후에 자원 관리 객체에게 전달(자원 획득 즉 초기화, RAII)2. 자원 관리 객체는 자신의 소멸자에서 자원이 해제되는 것을 보장

‘구멍 막기 전문가’==자원 관리 객체!

알아서 지워 주기 때문에Smart Pointer라는 명칭 사용

Page 9: Effective c++ chapter3, 4 요약본

그런데…알아서 잘하면 하나로 충분하지 않나요?왜 둘이나 되죠?

std에서 지원하는 std::auto_ptr에 제약사항이 많아Boost에서 기능을 추가해 내놓은 것이 td1::shared_ptr

std::auto_ptr 요구사항• 대입 연산자 사용은 2중 delete 가능성이 있어

기존 smart pointer가 null로 변경• 한 객체를 둘 이상의 smart pointer가 물면 안 됨

td1::auto_ptr 기능• 어떤 자원을 가리키는 횟수를 확인 할 수 있음• 해당 자원을 아무도 가리키지 않는 경우 제거

Page 10: Effective c++ chapter3, 4 요약본

자동으로 하니까 편하네!이제 끝?

‘아니다’

여전히

‘인간의 실수’가 남았다

Page 11: Effective c++ chapter3, 4 요약본

std::auto_ptr<std::string> aps(new std::string[10]);

std::tr1::shard_ptr<int> spi(new int[1024]);

스마트한 녀석들이니깐알아서 잘 생성하고 지우겠지?!

아니다!스마트한 녀석도 불러줘야 일을 하지…

생성을 몇 개 했는가?삭제는 몇 개 했는가?

Page 12: Effective c++ chapter3, 4 요약본

배열처럼 동시에 여러 생성자가 호출 되면

‘소멸자가 호출되는 횟수’가 중요하다

하나의 객체와 여러 개의 객체는생긴 것부터 다르다

즉,new와 delete는 짝을 맞춰야 한다

스마트한 녀석들이라도 반드시!

Page 13: Effective c++ chapter3, 4 요약본

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에게 포인터가 배열을 가리킴을 알림

Page 14: Effective c++ chapter3, 4 요약본

그런데…스마트한 녀석들도 사고 치지 않을까?날 믿어! 라고 외치던 라이브러리에뒤통수 맞아 봐야 정신 차리지?

그래서! 준비 했습니다

외부에서 자원 관리 클래스 파고 들기

Page 15: Effective c++ chapter3, 4 요약본

//daysHeld 전방 선언int daysHels(const Investment *pi);

std::tr1::shared_ptr<Investment> pInv( createInvestment() );

int days = daysHeld(pInv);

현재 전달인자의 타입은 무엇인가?

1. Investment*2. tr1::shared_ptr<investment>

Page 16: Effective c++ chapter3, 4 요약본

잊으셨나본데…스마트한 녀석들 == ‘자원 관리 객체‘당신이 생각하는 그 객체가 아니다

당근 착하지도 않다!알아서 바꿔주는 것도 하지 않는다원래 똑똑한 놈들이 더 악랄하지 않던가별도의 접근 방법이 필요하다

Page 17: Effective c++ chapter3, 4 요약본

실제 객체에 접근하기 위한 방법

1. 자원 관리 객체가 제공하는 get() 메소드를 사용2. 자원 관리 객체에 operator ->, operator *를 써서 직접 접근3. 클래스에 명시적 변환 함수로 get 제공4. 암시적 변환 함수로 접근

명시적 변환은 안전성 확보암시적 변환은 고객 편의성

각각 장점과 단점이 존재함

Page 18: Effective c++ chapter3, 4 요약본

지금까지 힙 기반 자원에 대해 확인했는데항상 힙에만 자원이 생길까?

출처 -http://donghwada.tistory.com/93

아니다!힙이 아니면 스마트도 소용 없음자원 관리 클래스를 직접 만들어야…

Page 19: Effective c++ chapter3, 4 요약본

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); //얘 어째야 할까요?

}

만약 없었다면 임계영역(블록 영역)을 벗어나며자동으로 해제 되어 잘 구동 되었을 것임

Page 20: Effective c++ chapter3, 4 요약본

모든 방법을 다 고려해보자!

• 복사를 못하게 방지한다• 관리하고 있는 자원에 대해 참조 카운팅 수행• 관리하고 있는 자원을 진짜로 복사(deep copy)• 관리하는 자원의 소유권 이전

Be OK!하고 나서 이후 조치를 알아서 해줘야 함복사 금지/ 참조 카운팅 수행 추천

Page 21: Effective c++ chapter3, 4 요약본

또 항상 문제가 되는 실행순서에 집중하자!new로 생성한 객체를 스마트 포인터에 저장하는 도중에…

생성하기도 전에 객체를 스마트 포인터에 밀어 넣으면?

터진다!컴파일러 제작사가 프로그램을 좌지우지호출 순서에 의해 문제 발생

Page 22: Effective c++ chapter3, 4 요약본

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은 ‘낙동강 오리알’

Page 23: Effective c++ chapter3, 4 요약본

processWidget( std::tr1::shared_ptr<Widget>( new Widget ), priority() );

std::tr1::shared_ptr<Widget> pw(new Widget);

processWidget( pw, priority() );

한 줄로 스스로를 구원하라

Page 24: Effective c++ chapter3, 4 요약본

OOP결국 설계는 객체 지향객체화를 얼마나 잘했는지가 중요함

정확성, 효율캡슐화, 유지보수성, 확장성규약 준수 등

Page 25: Effective c++ chapter3, 4 요약본

‘제대로 쓰기에 쉽고 엉터리로 쓰기에 어려운‘

인터페이스를 만들어라

각 객체를 연결하는 인터페이스를 관리 하는 자가설계(혹은 사용자)를 지배한다

• 범위를 벗어난 값이 들어오면 오류• 잘못된 타입이 들어오면 오류• 일관성 있는 인터페이스 만들기• 사용자가 실수할 가능성이 있는 부분을 제거

(미리 auto_ptr 또는 tr1::shared_ptr로 감싸기)

Page 26: Effective c++ chapter3, 4 요약본

‘좋은’타입 설계하기클래스도 결국 내부가 복잡한 타입좋은 타입을 설계하면 모든 것이 해결

효과적인 타입 설계의 조건

• 문법이 자연스럽고• 의미구조가 직관적이며• 효율적인 구현

Page 27: Effective c++ chapter3, 4 요약본

어디서 많이 보던 장면

출처 –질풍기획 시즌2

Page 28: Effective c++ chapter3, 4 요약본

새로 정의한 타입의 객체 생성 및 소멸은 어떻게 이뤄져야 하는가?객체 초기화는 객체 대입과 어떻게 달라야 하는가?

새로 만드는 타입이 얼마나 일반적인가?정말로 꼭 필요한 타입인가?

질답이 책 내용 대부분입니다공부합시다

Page 29: Effective c++ chapter3, 4 요약본

전체 그림은 그렇다치고각 객체간 전달 방식은 어떻게 해야 될까요?

1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’2. 함수 리턴값을 참조자로 선택하지 말자

둘 하는 이야기가 반대 아닌가요?

Page 30: Effective c++ chapter3, 4 요약본

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;

};

Page 31: Effective c++ chapter3, 4 요약본

1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’2. 함수 리턴값을 참조자로 선택하지 말자

bool validateStudent( student s);

• student 복사 생성자, 소멸자 호출• student string 객체 생성, 소멸• person 복사 생성자, 소멸자 호출• person string 객체 생성, 소멸

• 총 비용 생성자 6회, 소멸자 6회

bool validateStudent( const student& s);

• 참조형이라 이전에 생성 된 것을 사용• 복사손실 문제도 사라짐

(복사손실 문제 – 자식 클래스가 부모클래스로 형변환등을 거치며 정상 구동하지 않는 것)

이외에도지속적인 비용문제, 레지스터 입력 등효율성 측면에서 반드시 필요

Page 32: Effective c++ chapter3, 4 요약본

1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’2. 함수 리턴값을 참조자로 선택하지 말자

주의!

기본제공 타입, STL 반복자, 함수 객체 타입위 상황에서는 ‘값에 의한 전달’이 적절

Page 33: Effective c++ chapter3, 4 요약본

1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’2. 함수 리턴값을 참조자로 선택하지 말자

엄청난 효율!이것을 정녕 포기하란 말입니까?

Page 34: Effective c++ chapter3, 4 요약본

1. ‘값에 의한 전달’ 보다는 ‘상수 객체 참조자에 의한 전달’2. 함수 리턴값을 참조자로 선택하지 말자

스택 영역 특징• 함수가 시작 될 때 같이 늘어남• 함수가 종료 될 때 같이 소멸됨

값을 함수에 넘겨주면return 객체를 함수에서 생성해야 함

함수 내부에서 생성하는 것은 스택에 들어감return 객체도 스택에 들어감

함수가 종료되며 스택 영역 소멸return 객체도 소멸?!

이미 당신은 없는 값에 접근중!

Page 35: Effective c++ chapter3, 4 요약본

캡슐화(Encapsulation)각 객체는 최대한 서로 배제 되어야 함객체 인터페이스를 통일특히, 함수로 통일해야 함

동시에 모든 데이터 멤버를 숨긴다서로의 의존성을 줄이기 위해!

Page 36: Effective c++ chapter3, 4 요약본

따라서!

모든 데이터 멤버(멤버 변수)는 private 영역

클래스 제작자는 문법적으로 일관성 유지세밀한 접근 제어 가능내부 구현의 융통성 발휘

protected도 있잖아요?는 훼이크닷

자식 클래스에서 접근하는 것도문법적 일관성과 세밀한 접근 측면에서public과 동일

Page 37: Effective c++ chapter3, 4 요약본

같은 맥락에서

비멤버 비프렌드 함수를 선호하라

패키징 유연성이 높아지며컴파일 의존도를 낮출 수 있음비멤버 함수인 경우 자체 확장성도 확보

프렌드는 ‘일진’프렌드다선언하는 객체는 빵셔틀일뿐…(친구 아이다 아이가)

프렌드 선언하면 내부를 탈탈 털어줌private까지 싸그리 털림간편함에 속지 말자

Page 38: Effective c++ chapter3, 4 요약본

비멤버 함수는 멀티 플레이어

타입 변환이 모든 매개변수에 적용하는 경우비멤버 함수를 선언

비멤버 함수가 아닌 경우

• 인자 배치 순서에 따라서 실행 일관성이 깨짐• 혼합형 수치 연산에서도 실행 일관성이 깨짐

Page 39: Effective c++ chapter3, 4 요약본

SWAP 함수 효율화 하기

swap은 일상적이지만 입력 객체에 따라 성능이 휘청임부모, 자식 객체등을 반복 복사

표준 swap, 멤버 swap, 비멤버 swap, 특수화한 std::swap각 상황별로 나눠 사용할 수 있도록 함

• 일단 표준형에서 효율이 좋으면 현상태 유지• 그러나 효율이 좋지 않다면

1. 객체 내부에 swap 함수를 제작2. 해당 클래스에 비멤버 swap을 제작해 넣음3. 객체 내부 swap 함수를 비멤버 함수가 호출하도록 함4. 새로운 클래스에 대한 std::swap 특수화 버전 준비5. 특수화 버전에서도 객체 내부 swap 함수 호출6. 사용자가 swap 호출시 호출함수에 namespace 사용을 선언

Page 40: Effective c++ chapter3, 4 요약본

주의!

다 좋은데 std에 뭔가를 새로 추가하려 하지 말 것std에 추가는 순간 실행되는 결과가 미정의 됨

Page 41: Effective c++ chapter3, 4 요약본

chapter 3, 4 끝