흔히 책상을 메모리에빗대는 경우가 많은데
아래 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 기능
• 어떤 자원을 가리키는 횟수를 확인 할 수 있음
• 해당 자원을 아무도 가리키지 않는 경우 제거
그런데…
스마트한 녀석들도 사고치지 않을까?
날 믿어! 라고 외치던 라이브러리에
뒤통수 맞아 봐야 정신 차리지?
그래서! 준비 했습니다
외부에서 자원 관리 클래스 파고 들기
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은 ‘낙동강 오리알’
OOP
결국 설계는 객체지향
객체화를 얼마나 잘했는지가 중요함
정확성, 효율
캡슐화, 유지보수성, 확장성
규약 준수 등
25.
‘제대로 쓰기에 쉽고엉터리로 쓰기에 어려운‘
인터페이스를 만들어라
각 객체를 연결하는 인터페이스를 관리 하는 자가
설계(혹은 사용자)를 지배한다
• 범위를 벗어난 값이 들어오면 오류
• 잘못된 타입이 들어오면 오류
• 일관성 있는 인터페이스 만들기
• 사용자가 실수할 가능성이 있는 부분을 제거
(미리 auto_ptr 또는 tr1::shared_ptr로 감싸기)
26.
‘좋은’타입 설계하기
클래스도 결국내부가 복잡한 타입
좋은 타입을 설계하면 모든 것이 해결
효과적인 타입 설계의 조건
• 문법이 자연스럽고
• 의미구조가 직관적이며
• 효율적인 구현
새로 정의한 타입의객체 생성 및 소멸은 어떻게 이뤄져야 하는가?
객체 초기화는 객체 대입과 어떻게 달라야 하는가?
…
새로 만드는 타입이 얼마나 일반적인가?
정말로 꼭 필요한 타입인가?
질답이 책 내용 대부분입니다
공부합시다
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 객체도 소멸?!
이미 당신은 없는 값에 접근중!
따라서!
모든 데이터 멤버(멤버변수)는 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에 추가는 순간 실행되는 결과가 미정의 됨