[C++][Effective C++] 항목 45 : "호환되는 모든 타입"을 받아들이는 데는 멤버 함수 템플릿이 직방!

WestCoast·2022년 5월 1일
0

C, C++

목록 보기
10/12

이번 항목의 주요 포인트

  • 사용자정의 스마트포인터로 포인터의 암시적 변환을 따라해보자
  • 스마트포인터의 암시적 변환 설계를 알아보자
  • 멤버 함수 템플릿을 사용해보자

1. 사용자정의 스마트포인터


using namespace std;

class CBase
{ };
class CDerived : public CBase
{ };

template<typename T>
class SmartPtr
{
public:
	explicit SmartPtr(T* ptr)
	{ }
};

int main()
{
	SmartPtr<CBase> ptBase = SmartPtr<CBase>(new CBase);

	// Error!) 컴파일러 관점에서 둘은 그냥 남남임
	// 즉, 둘이 상속 관계인지 아닌지 알지도 못한다는 것
	SmartPtr<CBase> ptDerived = SmartPtr<CDerived>(new CDerived);
}

2. 복사생성자


using namespace std;

class CBase
{ };
class CDerived : public CBase
{ };

// 새로운 파생 클래스 등장
class CDerived2 : public CBase
{ };

template<typename T>
class SmartPtr
{
public:
	explicit SmartPtr(T* ptr)
	{ }

	// 복사생성자를 만들어줬다
	SmartPtr(const SmartPtr<CDerived>& rhs)
	{ }
};

int main()
{
	SmartPtr<CBase> ptBase = SmartPtr<CBase>(new CBase);

	// 이제 정상적으로 받아줄 수 있다.
	SmartPtr<CBase> ptDerived = SmartPtr<CDerived>(new CDerived);

	// Error!) 컴파일러: CDerived에 대해 복사생성자를 만들어준 거지, 
	// 다른 파생클래스에 대해서는 안 만들어줬잖아.
	SmartPtr<CBase> ptDerived2 = SmartPtr<CDerived2>(new CDerived2);
}

3. 일반화 복사생성자


using namespace std;

class CBase
{ };
class CDerived : public CBase
{ };

class CDerived2 : public CBase
{ };

// 이번엔 아무런 관계도, 근본도 없는 클래스 등장
class COther
{ };

template<typename T>
class SmartPtr
{
public:
	explicit SmartPtr(T* ptr)
	{ }

	// '일반화' 복사생성자를 만들어줬다
	template<typename U>
	SmartPtr(const SmartPtr<U>& rhs)
	{ }
};

int main()
{
	SmartPtr<CBase> ptBase = SmartPtr<CBase>(new CBase);

	SmartPtr<CBase> ptDerived = SmartPtr<CDerived>(new CDerived);

	// 이제 정상적으로 받아준다.
	SmartPtr<CBase> ptDerived2 = SmartPtr<CDerived2>(new CDerived2);

	// 아무런 (상속)관계도 없는 클래스까지 이제 넣어줄 수 있게 되었다.
	// 이런 무제한적인 자유까지 원한 건 아니었는데...
	SmartPtr<CBase> ptOther = SmartPtr<COther>(new COther);
}

4. 일반화 복사생성자의 자유도 제한


using namespace std;

class CBase
{ };
class CDerived : public CBase
{ };

class CDerived2 : public CBase
{ };

// 이번엔 아무런 관계도, 근본도 없는 클래스 등장
class COther
{ };

template<typename T>
class SmartPtr
{
public:
	explicit SmartPtr(T* ptr)
		: realPtr(ptr)
	{ }

	// 이제 컴파일러님의 힘을 빌려서 realPtr과 rhs.getPtr() 이 
	// 서로 암시적 변환이 가능할 때만 컴파일 되도록 만들어주었다.
	template<typename U>
	SmartPtr(const SmartPtr<U>& rhs)
		: realPtr(rhs.getPtr())
	{ }

	// 쌩 포인터를 반환해주는 get함수
	T* getPtr() const
	{
		return realPtr;
	}

private:
	T* realPtr = nullptr;
};

int main()
{
	SmartPtr<CBase> ptBase = SmartPtr<CBase>(new CBase);

	SmartPtr<CBase> ptDerived = SmartPtr<CDerived>(new CDerived);

	SmartPtr<CBase> ptDerived2 = SmartPtr<CDerived2>(new CDerived2);

	// Error!) CBase* 포인터와 COther* 포인터는 암시적 변환이 불가능한 관계임!
	// 서로 일면식도 없는 남남임!
	SmartPtr<CBase> ptOther = SmartPtr<COther>(new COther);
}
profile
게임... 만들지 않겠는가..

0개의 댓글