问题实例
所谓智能指针,指的是一些行为像指针的对象,它们可以提供指针没有的机能。STL容器的迭代器几乎总是智能指针,你不会奢望使用operator++将指针从list的一个node移至另一个node,但是迭代器可以。
真实指针有一个很好用的功能:支持隐式转换。举例来说:
- derived class指针可以隐式转为base class指针
- 指向non-const对象的指针可以指向const对象
既然智能指针是行为如同原始指针的对象,那么我们当然希望智能指针也具备上述功能。具体来说,我们希望下述代码能够通过编译:1
2
3
4
5
6
7
8
9class Top;
class Middle:public Top;
template<typename T>
class Smartptr{//自定义的智能指针
public:
explicit Smartptr(T* realPtr);
}
Smartptr<Top> pt1 =Smart<Middle>(new Middle);
Smartptr<const Top> pt2 = pt1;
但是,同一个template的不同具现化之间并不存在继承关系,所以,smartptr<top>
与smartptr<middle>
完全无关,为了达到我们期望的smart classes之间的转换能力,我们必须明确地编写它们。
解决方案
我们关注的重点是应该如何编写智能指针的构造函数。经过分析不难得出结论:我们永远无法写出所有需要的构造函数。我们真正需要撰写的是一个构造模板。
构造函数模板具体如下:1
2
3
4
5
6template<typename T>
class SmartPtr{
public:
template<typename U>
SmartPtr(const SmartPtr<U>& other);
}
方案剖析
该构造函数模板声明:对于任何类型T和任何类型U,总可以根据一个SmartPtr<U>
对象来构造一个SmartPtr<T>
对象。我们把这种构造函数称为泛化copy构造函数。之所以不用explicit声明,是因为原始指针之间的转换就是隐式的,我们需要保证智能指针与原始指针之间的兼容性。
声明固然已经完成,但是实现却值得花一番心思。并非任意的U型智能指针都能转为T型智能指针(比如把base转为derived),所以我们必须在某方面对这一member template所创建的成员函数群进行筛选。
解决方案的实现
假设SmartPtr遵循shared_ptr的接口,存在一个get函数返回原始指针的副本,那我们则可以在构造模板中实现代码约束,具体如下:1
2
3
4
5
6
7
8
9
10template <typename T>
class SmartPtr{
public:
template <typename U>
SmartPtr(const SmartPtr<U> &other)
:helder(other.get()) {....}
T* get() const {return heldptr;}
private:
T* heldPtr;
}
现在,仅在“存在某个隐式转换将U*指针转为T*指针”时才能通过编译,而这正是我们想要的。
其它
member function template不仅仅单纯用于构造函数,它还常常在赋值操作时发挥作用。
在class中声明泛化copy构造函数并不会阻止编译器生存自己的copy构造函数(一个non-template),因此如果需要控制copy构造的方方面面(比如禁止拷贝之类),我们必须同时声明泛化copy构造函数与non-template构造函数,同样的规则也适用于赋值操作。
总结
- 可使用member function template生成“可接受所有兼容类型”的函数
- 如果你声明了member template用于泛化操作,你还是需要声明正常的copy构造函数与copy assignment运算符。