这是我在阅读Effective c++中认为比较重要的部分,下面给出了我对这一节的理解,并写出对应的比较容易理解的代码。
真实指针做得很好的一件事是,支持隐式转换,继承类指针可以隐式转换为基类指针,指向non-const对象的指针可以转换为指向const对象的指针。
例如有下面的一个三层继承体系中的一些转换:
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:
explicit SmartPtr(T* realPtr);
...
};
SmartPtr<Top>pt1 = SmartPtr<Middle>(new Middle);
SmartPtr<Top>pt2 = SmartPtr<Bottom>(new Bottom);
SmartPtr<Top>pct2 = pt1;
但是同一个template的不同具现体之间并不存在什么与生俱来的固有关系,本例中就是SmartPtr<top>和SmartPtr<Middle>、SmartPtr<Bottom>之间并不存在什么关系。编译器视它们为完全不同的class。因此我们需要获得这个转换能力,解决方法是使用成员函数模板;如下:
template<typename T>
class SmartPtr {
public:
template<typename U>
SmartPtr(const SmartPtr& other);
...
};
以上代码的意思是,对任何类型T和任何类型U,这里可以根据SmartPtr<U>生成一个SmartPtr<T>。 我们称此构造函数为泛化拷贝构造函数。
我们希望这个泛化拷贝构造函数能够遵守一定的规则,例如我们不希望父类可以向子类转换,我们也不希望根据SmartPtr<double>去创建一个SmartPtr<int>。我们通过对内置指针提供get和初始化列表来达到这个目的。例如下:
template<typename T>
class SmartPtr {
public:
SmartPtr(T*p) :heldPtr(p) {}
template<typename U>
SmartPtr(const SmartPtr<U>& other):heldPtr(other.get()){ }
T* get()const {
return heldPtr;
}
private:
T* heldPtr;
};
这个行为只有当“存在某个隐式转换可将一个U*指针转为一个T*指针”时才能通过编译,而这正是我们想要的。
请记住
请使用member function template(成员函数模板)生成"可几首所有兼容类型"的函数
如果你声明member template用于"泛化copy构造"或"泛化assignment操作",你还需要声明正常copy构造函数和copy assignment操作符.