C++ 기본 제공(bult-in) 포인터(이하 벙어리(dumb) 포인터) 대신 스마트 포인터를 사용하는 세가지 이유
- 생성(construction)과 소멸(destruction) 작업을 조절할 수 있다.
리소스 누수를 막는 것 - 복사(copy)와 대입(assignment) 동작을 조절할 수 있다.
- 역참조(dereferencing) 동작을 조절할 수 있다.
스마트 포인터는 C++ 기본 제공 포인터처럼 가리킬 타입이 정확하게 지정되야함
- 타입제약이 엄격하다(strongly typed)
스마트 포인터의 생성, 대입, 소멸
스마트 포인터의 생성, 대입, 소멸은 소유권(ownership)이라는 것 때문에 꽤 복잡함.
auto_ptr은 대입시 LHS 변수에 소유권이 넘어가게되고 RHS 변수의 포인터에는 NULL이 들어간다.
함수 호출시 auto_ptr을 값으로 전달하게 되면 포인터가 원하지 않게 사라질 수 있다.
이런 경우 pass by reference to const를 통해 전달하도록 해야한다.
- template<class T>
- auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs)
- {
-
if(this == &rhs) // auto_ptr을 짜라고 했을 때 영형이 빠트릴 수 있는 부분이다. 나도ㅋㅋㅋㅋ
-
return *this;
-
....
- }
영형이 실수하기 쉬운 부분이라 얘기하고 보니 복사생성자 처리할 때도 실수하는 부분인 것 같다.
소멸자의 형태
- template <class T>
- SmartPtr<T>::~SmartPtr()
- {
-
if(*this가 *pointee를 소유하면)
-
delete pointee;
- }
항목 29에서 참조 카운팅 기능을 이용해서 구현하자.
역참조(Dereferencing) 연산자 구현하기
operator*에 반환 타입이 Reference인 이유
값에 의한 반환을 하게되면 정확성(슬라이스 문제)과 효율(임시객체 생성)의 문제가 발생함
널 포인터를 역참조한 결과는 정의되어 있지 않기 때문에, '잘못 된' 동작만 없으면 된다.
널 포인터 역참조를 시도하면 throw해서 죽이던지 abort 하던지 맘대로 예외처리 하면 됨
operator->도 구현해주자
특별한 문제가 없는 함수
스마트 포인터가 널(null)인지 점검하기
스마트 포인터에 IsNull과 같은 메소드를 넣기
-> 벙어리 포인터와 똑같이 동작할 수 없음
operator void*()를 통해 암시적 변환하기
- if(ptr == 0) ... // 정상 작동
- if(ptn) ... // 정상 작동
- if(!ptrn) ... // 정상 작동
서로 다른 타입의 스마트 포인터까지 비교가 가능해지는 어처구니 없는 일이 생김.
- SmartPtr<Apple> pa;
- SmartPtr<Orange> po;
- if(pa == po) // 이게 컴파일이 됨.
operator! 연산자를 오버로딩해서 포인터가 널일 때만 true를 반환하도록 하기
- if(ptr == 0) ... // 안됨
- if(ptn) ... // 안됨
- if(!ptrn) ... // 정상 작동
-
SmartPtr<Apple> pa;
SmartPtr<Orange> po;
if(!pa == !po) // 여전히 컴파일 됨.
한정치산자나 금치산자가 아닌 한 !pa == !po같이 코딩하는 경우는 드물기 때문에 필자의 마음이 조금 가벼워졌다고 한다.
스캇마이어 이세낀 결론이 없다. 아 놔....
스마트 포인터를 벙어리 포인터로 변환하기
- template<class T>
- class SmartPtr
- {
- ...
- operator T*() { return pointee; }
- ...
- };
암시적 타입변환을 통해 벙어리 포인터를 조작했다가는 참조 카운팅에 필요한 내부 정보가 조정되지 않아 끔찍한 일이 발생할 문제가 있음.
위와 같은 암시적 타입변환 코드는 아래의 코드 처럼 문제를 발생 시킬 수 있음
- class M
- {
- ...
-
M(const KK* p);
- ...
- }
- M merge(const M& kk1, const M& kk2);
- SmartPtr<KK> a, b;
- ...
- merge(a, b); // 암시적 타입변환이 두번일어나지 않기 때문에 에러
- SmartPtr<KK> a(new KK);
- delete a; // 후... 위험
꼭 암시적 타입변환을 해야할 이유가 없으면, 스마트 포인터를 벙어리 포인터로 바꾸는 암시적 타입변환 연산자는 제공하지 말아야한다.
스마트 포인터와 상속 기반의 타입변환
스마트 포인터 클래스 각각에다가 암시적 타입연산자를 만들어 넣어 다른 스마트 포인터 클래스로 암시적 변환을 가능하게 할 때의 문제점
-
SmartPtr 클래스를 타입 매개변수를 통해 만들어야 하기 때문에 필요한 암시적 타입변환 연산자를 추가할 수 있는 것이지만, 템플릿의 취지를 흔드는 결과를 낳음
-
가리키는 객체가 가지고 있는 클래스 계통 구조가 깊으면 그만큼 더 많은 변환 연산자를 추가해야함.
- 사용자 정의 타입변환을 한 번에 두 번 이상 할 수 없음.
- template<class T>
- class SmartPtr
- {
- ...
-
template<class newType>
-
operator SmartPtr<newType>()
-
{
-
return SmartPtr<newType>(pointee);
-
}
- ...
- };
맴버 함수를 통한 타입변환의 문제점
- 스마트 포인터에서 맴버함수를 써서 타입변환을 수행하기 때문에, C++ 컴파일러의 눈에는 모든 변환 함수가 똑같아 보이는 경우가 발생할 수 있음.
결국 모호(ambiguous)하다는 에러를 내보내게 됨. - 맴버 템플릿을 제대로 지원하는 환경이 많지 않기 때문에 이식성이 떯어짐.
98년 이후의 C++ 표준안을 반영한 C++ 컴파일러는 모두 이 기능을 지원 - 동작 원리를 제대로 이해하기 힘듬.
스마트 포인터 클래스가 상속 기반의 타입변환에 대해서도 벙어리 포인터처럼 동작하도록 구현할 수는 없음.
모호성 문제는 적절한 캐스팅을 통해 해결해야함.
스마트 포인터와 const
- SmartPtr<CD> pCD = new CD("KKK K K K");
- SmartPtr<const CD> pConstCD = pCD;
껍데기가 같다고 타입까지 같지는 않음.
비상수를 상수로 바꾸는 것은 안전하지만, 상수를 비상수로 바꾸는 것은 위험하다.
Const의 규칙이 Public 상속 규칙과 흡사한 느낌
- template <class T>
- class SmartPtrToConst
- {
- ...
- proctected:
-
union
-
{
-
const T* constPointee;
-
T* pointee;
-
};
- }
- template <class T>
- class SmartPtr
-
: public SmartPtrToConst<T>
- {
-
...
- };
union을 통해서 메모리가 공유되면서 타입은 별개인 pointer를 만들고 const일 때와 아닐때를 나누어 처리.
위의 코드는 아래처럼 바꾸면 됨
- SmartPtr<CD> pCD = new CD("KKK K K K");
- SmartPtrToConst<CD> pConstCD = pCD;
총정리
스마트 포인터는 구현하기 까다롭고, 이해하기도 쉽지 않으며 이후의 유지 보수도 어렵다.
스마트 포인터를 사용한 코드는 보통의 벙어리 포인터를 사용한 코드보다 디버깅이 훨씬 어렵다.
아무리 노력해도 벙어리 포인터를 완벽하게 대신할 수 있는 일반적인 용도의 스마트 포인터는 절대로 설계할 수 없는 점을 잊어선 안된다.
그럼에도 불구하고 스마트 포인터는 다른 방법으론 구현하기 힘든 기능을 구사하는데 효과적이다.
'약 좋다고 남용말고 약 모르고 오용말자'라는 표어도 있듯이, 스마트 포인터도 적절한 상황에 따라 사용해야 한다
'기타' 카테고리의 다른 글
C++에서 스마트 포인터 템플릿 (0) | 2013.11.14 |
---|---|
C++ - 스마트 포인터 커스터마이즈 (0) | 2013.11.14 |
MidiBanks.mid 모음 [지금까지 맘에드는 곡 모은 것] (0) | 2012.11.29 |
흠 시티즌2 빌더는 나오기 아직 멀었네요.. (0) | 2012.09.28 |
Chair 1.4 플러그인 파일입니다. (0) | 2012.09.24 |