일지

Effective C++...43

niamdank 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;

};


 

마지막으로 TU가 같은 경우를 제대로 처리하기 위해서는 기본 복사 생성자들도 생성을 해줘야 한다. C++의 원칙에 따라 복사 생성자를 사용자화 했을 때 기본 복사 생성자는 만들어지지 않기 때문이다.