-
Effective C++...43일지 2022. 8. 19. 07:27
"호환되는 모든 타입"을 받아들이는 데는 멤버 함수 템플릿이 직방!
스마트 포인터와 일반 포인터
기본적으로 스마트 포인터는 "++" 연산을 통해 다음 노드로 이동하는 등 포인터에서 지원하지 않는 다양한 기능을 지원한다. 다만 일반 포인터와 달리 스마트 포인터는 암시적 변환을 지원하지 않는다.
class Top{ ... };
class Middle : public Top{ ... };
class Bottom : public Middle{ ... };
// 스마트 포인터는 다음과 같은 암시적 변환이 지원되지 않는다.
Top* pt1 = new Middle;
Top* pt2 = new Bottom;
const Top* pct2 = pt1;
스마트 포인터의 암시적 변환 지원
이를 위해서 특정 타입의 생성자를 만드는 게 아닌 생성자를 만들어주는 템플릿 함수, 멤버 함수 템플릿을 응용하면 된다.
template<typename T>
class SmartPtr {
public:
template<typename U>
SmartPtr(const smartPtr<U>& other); // 복사 생성자를 자동 생성한다.
};
다만 이런식으로 사용하면 기본 클래스로의 변환 외에도 기본 클래스에서 파생 클래스로의 변환도 허용되고, 심지어 int*에서 double*로 변환도 발생할 수 있다.
이를 막기 위해서는 SmartPtr내부에 T타입 포인터를 두고 이를 초기화 해주는 방식으로 T로 암시적 변환이 가능한 것에 대해서만 컴파일이 되도록 만들면 된다.
template<typename T>
class SmartPtr {
public:
template<typename U>
SmartPtr(const smartPtr<U>& other)
: heldPtr(other.get()) { ... }
T* get() const { return heldPtr; }
private:
T* heldPtr;
};
마지막으로 T와 U가 같은 경우를 제대로 처리하기 위해서는 기본 복사 생성자들도 생성을 해줘야 한다. C++의 원칙에 따라 복사 생성자를 사용자화 했을 때 기본 복사 생성자는 만들어지지 않기 때문이다.