副标题#e#
smart pointers(智能指针)是行为很像指针可是增加了指针没有提供的成果的 objects。譬喻,《C++箴言:利用工具打点资源》叙述了尺度 auto_ptr 和 tr1::shared_ptr 是奈何被应用于在得当的时间自动删除的 heap-based resources(基于堆的资源)的。STL containers 内的 iterators(迭代器)险些始终是 smart pointers(智能指针);你绝对不能指望用 "++" 将一个 built-in pointer(内建指针)从一个 linked list(线性链表)的一个节点移动到下一个,可是 list::iterators 可以做到。
real pointers(真正的指针)做得很好的一件事是支持 implicit conversions(隐式转换)。derived class pointers(派生类指针)隐式转换到 base class pointers(基类指针),pointers to non-const objects(指向很是量工具的指针)转换到 pointers to const objects(指向常量工具的指针),等等。譬喻,思量在一个 three-level hierarchy(三层担任体系)中能产生的一些转换:
class Top { ... };
class Middle: public Top { ... };
class Bottom: public Middle { ... };
Top *pt1 = new Middle; // convert Middle* => Top*
Top *pt2 = new Bottom; // convert Bottom* => Top*
const Top *pct2 = pt1; // convert Top* => const Top*
在 user-defined smart pointer classes(用户界说智能指针类)中仿照这些转换是需要能力的。我们要让下面的代码可以或许编译:
template<typename T>
class SmartPtr {
public: // smart pointers are typically
explicit SmartPtr(T *realPtr); // initialized by built-in pointers
...
};
SmartPtr<Top> pt1 = // convert SmartPtr<Middle> =>
SmartPtr<Middle>(new Middle); // SmartPtr<Top>
SmartPtr<Top> pt2 = // convert SmartPtr<Bottom> =>
SmartPtr<Bottom>(new Bottom); // SmartPtr<Top>
SmartPtr<const Top> pct2 = pt1; // convert SmartPtr<Top> =>
// SmartPtr<const Top>
在同一个 template(模板)的差异 instantiations(实例化)之间没有 inherent relationship(担任干系),所以编译器认为 SmartPtr<Middle> 和 SmartPtr<Top> 是完全差异的 classes,并不比(例如说)vector<float> 和 Widget 的干系更近。为了获得我们想要的在 SmartPtr classes 之间的转换,我们必需显式地为它们编程。
#p#副标题#e#
在上面的 smart pointer(智能指针)的示例代码中,每一个语句建设一个新的 smart pointer object(智能指针工具),所以此刻我们就会合于我们如何写 smart pointer constructors(智能指针的结构函数),让它以我们想要的方法运转。一个要害的事实是我们无法写出我们需要的全部 constructors(结构函数)。在上面的 hierarchy(担任体系)中,我们能从一个 SmartPtr<Middle> 或一个 SmartPtr<Bottom> 结构出一个 SmartPtr<Top>,可是假如未来这个 hierarchy(担任体系)被扩充,SmartPtr<Top> objects 还必需能从其它 smart pointer types(智能指针范例)结构出来。譬喻,假如我们厥后插手
class BelowBottom: public Bottom { ... };
我们就需要支持从 SmartPtr<BelowBottom> objects 到 SmartPtr<Top> objects 的建设,并且我们虽然不但愿为了做到这一点而必需改变 SmartPtr template。
概略上,我们需要的 constructors(结构函数)的数量是无限的。因为一个 template(模板)能被实例化而发生无数个函数,所以仿佛我们不需要为 SmartPtr 提供一个 constructor function(结构函数函数),我们需要一个 constructor template(结构函数模板)。这样的 templates(模板)是 member function templates(成员函数模板)(经常被恰如其分地称为 member templates(成员模板))——生成一个 class 的 member functions(成员函数)的 templates(模板)的典型:
template<typename T>
class SmartPtr {
public:
template<typename U> // member template
SmartPtr(const SmartPtr<U>& other); // for a "generalized
... // copy constructor"
};
这就是说对付每一种范例 T 和每一种范例 U,都能从一个 SmartPtr<U> 建设出一个 SmartPtr<T>,因为 SmartPtr<T> 有一个取得一个 SmartPtr<U> 参数的 constructor(结构函数)。像这样的 constructor(结构函数)——从一个范例是同一个 template(模板)的差异实例化的 object 建设另一个 object 的 constructor(结构函数)(譬喻,从一个 SmartPtr<U> 建设一个 SmartPtr<T>)——有时被称为 generalized copy constructors(泛型化拷贝结构函数)。
上面的 generalized copy constructor(泛型化拷贝结构函数)没有被声明为 explicit(显式)的。这是存心为之的。built-in pointer types(内建指针范例)之间的范例转换(譬喻,从派生类指针到基类指针)是隐式的和不需要 cast(强制转型)的,所以让 smart pointers(智能指针)仿照这一行为是公道的。在 templatized constructor(模板化结构函数)中省略 explicit 正好做到这一点。
#p#分页标题#e#
作为声明,SmartPtr 的 generalized copy constructor(泛型化拷贝结构函数)提供的对象比我们想要的还多。是的,我们需要可以或许从一个 SmartPtr<Bottom> 建设一个 SmartPtr<Top>,可是我们不需要可以或许从一个 SmartPtr<Top> 建设一个 SmartPtr<Bottom>,这就像颠倒 public inheritance(公有担任)的寄义(拜见《C++箴言:确保果真担任模仿“is-a”》)。我们也不需要可以或许从一个 SmartPtr<double> 建设一个 SmartPtr<int>,因为这和从 int* 到 double* 的 implicit conversion(隐式转换)是不相称的。我们必需设法过滤从这个 member template(成员模板)生成的 member functions(成员函数)的群体。
如果 SmartPtr 跟从 auto_ptr 和 tr1::shared_ptr 的脚步,提供一个返回被这个 smart pointer(智能指针)持有的 built-in pointer(内建指针)的拷贝的 get member function(get 成员函数)(拜见《C++箴言:在资源打点类中筹备会见裸资源》),我们可以用 constructor template(结构函数模板)的实现将转换限定在我们想要的范畴:
template<typename T>
class SmartPtr {
public:
template<typename U>
SmartPtr(const SmartPtr<U>& other) // initialize this held ptr
: heldPtr(other.get()) { ... } // with other's held ptr
T* get() const { return heldPtr; }
...
private: // built-in pointer held
T *heldPtr; // by the SmartPtr
};
我们通过 member initialization list(成员初始化列表),用 SmartPtr<U> 持有的范例为 U* 的指针初始化 SmartPtr<T> 的范例为 T* 的 data member(数据成员)。这只有在“存在一个从一个 U* 指针到一个 T* 指针的 implicit conversion(隐式转换)”的条件下才气编译,而这正是我们想要的。最终的结果就是 SmartPtr<T> 此刻有一个 generalized copy constructor(泛型化拷贝结构函数),它只有在传入一个 compatible type(兼容范例)的参数时才气编译。
member function templates(成员函数模板)的用途并不限于 constructors(结构函数)。它们的另一个常见的任务是用于支持 assignment(赋值)。譬喻,TR1 的 shared_ptr(再次拜见《C++箴言:利用工具打点资源》)支持从所有兼容的 built-in pointers(内建指针),tr1::shared_ptrs,auto_ptrs 和 tr1::weak_ptrs结构,以及从除 tr1::weak_ptrs 以外所有这些赋值。这里是从 TR1 类型中摘录出来的一段关于 tr1::shared_ptr 的内容,包罗它在声明 template parameters(模板参数)时利用 class 而不是 typename 的偏好。(就像《C++箴言:领略typename的两个寄义》中叙述的,在这里的上下文情况中,它们的寄义严格一致。)
template<class T> class shared_ptr {
public:
template<class Y> // construct from
explicit shared_ptr(Y * p); // any compatible
template<class Y> // built-in pointer,
shared_ptr(shared_ptr<Y> const& r); // shared_ptr,
template<class Y> // weak_ptr, or
explicit shared_ptr(weak_ptr<Y> const& r); // auto_ptr
template<class Y>
explicit shared_ptr(auto_ptr<Y>& r);
template<class Y> // assign from
shared_ptr& operator=(shared_ptr<Y> const& r); // any compatible
template<class Y> // shared_ptr or
shared_ptr& operator=(auto_ptr<Y>& r); // auto_ptr
...
};
除了 generalized copy constructor(泛型化拷贝结构函数),所有这些 constructors(结构函数)都是 explicit(显式)的。这就意味着从 shared_ptr 的一种范例到另一种的 implicit conversion(隐式转换)是被答允的,可是从一个 built-in pointer(内建指针)或其 smart pointer type(智能指针范例)的 implicit conversion(隐式转换)是不被许可的。(explicit conversion(显式转换)——譬喻,经过一个 cast(强制转型)——照旧可以的。)同样引起留意的是 auto_ptrs 被传送给 tr1::shared_ptr 的 constructors(结构函数)和 assignment operators(赋值操纵符)的方法没有被声明为 const,于此比较的是 tr1::shared_ptrs 和 tr1::weak_ptrs 的被传送的方法。这是 auto_ptrs 被复制时需要唯一无二的被改变的事实的一个一定功效(拜见《C++箴言:利用工具打点资源》)。
#p#分页标题#e#
member function templates(成员函数模板)是一个极好的对象,可是它们没有改变这个语言的根基法则。《C++箴言:相识C++偷偷加上和挪用了什么》叙述的编译器可以发生的四个 member functions(成员函数)个中两个是 copy constructor(拷贝结构函数)和 copy assignment operator(拷贝赋值运算符)。tr1::shared_ptr 声明白一个 generalized copy constructor(泛型化拷贝结构函数),并且很明明,当范例 T 和 Y 沟通时,generalized copy constructor(泛型化拷贝结构函数)就能被实例化而成为 "normal" copy constructor(“通例”拷贝结构函数)。那么,当一个 tr1::shared_ptr object 从另一个沟通范例的 tr1::shared_ptr object 结构时,编译器是为 tr1::shared_ptr 生成一个 copy constructor(拷贝结构函数),照旧实例化 generalized copy constructor template(泛型化拷贝结构函数模板)?
就像我说过的,member templates(成员模板)不改变语言法则,并且法则划定假如一个 copy constructor(拷贝结构函数)是必须的而你没有声明,将为你自动生成一个。在一个 class 中声明一个 generalized copy constructor(泛型化拷贝结构函数)(一个 member template(成员模板))不会阻止编译器生成它们本身的 copy constructor(拷贝结构函数)(非模板的),所以假如你要全面支配 copy construction(拷贝结构),你必需既声明一个 generalized copy constructor(泛型化拷贝结构函数)又声明一个 "normal" copy constructor(“通例”拷贝结构函数)。这同样合用于 assignment(赋值)。这是从 tr1::shared_ptr 的界说中摘录的一段,可以作为例子:
template<class T> class shared_ptr {
public:
shared_ptr(shared_ptr const& r); // copy constructor
template<class Y> // generalized
shared_ptr(shared_ptr<Y> const& r); // copy constructor
shared_ptr& operator=(shared_ptr const& r); // copy assignment
template<class Y> // generalized
shared_ptr& operator=(shared_ptr<Y> const& r); // copy assignment
...
};
Things to Remember
·利用 member function templates(成员函数模板)生成接管所有兼容范例的函数。
·假如你为 generalized copy construction(泛型化拷贝结构)或 generalized assignment(泛型化赋值)声明白 member templates(成员模板),你依然需要声明 normal copy constructor(通例拷贝结构函数)和 copy assignment operator(拷贝赋值运算符)。