您的位置:首页 > 其它

条款45:运用成员函数模板接受所有兼容类型

2009-09-03 10:31 671 查看
条款44:运用成员函数模板接受所有兼容类型
(Use member function templates to accept "all compatible types.")

内容:
不知道大家注意到没有,在类的继承体系中,对象指针一直在为我们做一件很好的事情:支持隐式转换(implicit conversions).Derived class指针可以隐式转换为base class指针,"指向non-const对象"的指针可以转换为"指向const对象"......等等.下面例子展示了继承体系之间的可能进行的转换操作:
class Top{ ... };
class Middle:public Top{...};
class Bottom:public Middle{...};
Top* top1 = new Middle; //将Middle*转换为Top*
Top* top2 = new Bottom; //将Bottom*转换为Top*
const Top* const_top2 = top1; //将Top*转换为const Top*
在前面的条款13中我们提到,像std::auto_ptr和tr1::shared_ptr这样的智能指针,能够提供原始指针没有的机能.比如能够在正确的时机自动删除heap-based资源.本款中我们自然就想到了,如果智能指针也能支持上述的隐式操作转换,那岂不是很方便.于是我们在这里试图让类似下面的代码通过编译:
template <typename T>
class SmartPtr{
public:
explicit SmartPtr(T* realPtr);//智能指针通常以原始指针完成初始化
...
};
SmartPtr<Top> top1_smart_ptr = SmartPtr<Middle>(new Middle);
SmartPtr<Top> top2_smart_ptr = SmartPtr<Bottom>(new Bottom);
SmartPtr<const Top> const_top2_ptr = top1_smart_ptr;
但是,现实的情况好像并不是像我们想象的那么美好:同一个template的不同具现体(instantiation)之间并不存在故有的联系.也就是说,编译器视SmartPtr<Middle>和SmartPtr<Top>为完全不同的classes.它们之间没有任何必然的联系.这个问题不小啊,但一想到如果SmartPtr classes之间具有了这样的转换能力,世界将会变的更加的美好.我就增强了要实现这种模拟的信心.好,接着往下看.
在上述继承体系中,每一条语句都创建一个新式智能指针对象,我们自然就想到了,我们应该把工作的焦点放在如何编写智能指针的构造函数上,使其满足我们的转型需要.而稍微仔细的观察一会儿,我们就有了一个新的顾忌:我们永远无法写出我们需要的构造函数.为什么呢?因为继承基类的子类可以有很多个,每诞生一个新的子类,我们就必须要在基类智能指针中添加一个为实现其向子类转换的新的构造函数.那样的代码不仅理解起来很晦涩,更难以维护.在如此"绝境"之下我们想到了模板成员函数,有了这样的"利器"我们就在战术上从被动为主动了,哈哈.
template <typename T>
class SmartPtr{
public:
template <typename U>
SmartPtr(const SmartPtr<U>& other); //copy constructor
...
};
等一下,构造函数为什么没有explicit修饰?我故意的.因为要完成原始指针之间的隐式转换,我们需要支持这样的操作.如果SmartPtr也像auto_ptr和tr1::shared_ptr一样,提供一个get成员函数去发挥智能指针对象的原始指针副本.上面的代码我们可以写的更清楚一点:
template <typename T>
class SmartPtr{
public:
template <typename U>
SmartPtr(const SmartPtr<U>& other)
:held_ptr_( other.get() ){...} //用other.held_ptr_初始化this->held_ptr_,这里的other的原始对象如果是
//this原始对象的子类的话,这里就完成子类向父类的隐式转换过程.
T* get()const{ return held_ptr_;}
...
private:
T* held_ptr_; //这是SmartPtr持有的内置指针.
};
呵呵,不错吧!其实member function template(成员函数模板的效用不限于构造函数),它们常常扮演的另一个角色就是支持赋值操作.这点在TR1的shared_ptr中获得了绝好的发挥.下面是TR1规范中关于tr1::shared_ptr的一份摘录.
template<class T>
class shared_ptr{
public:
template<class Y>
explicit shared_ptr(Y* p);
template<class Y>
shared_ptr(shared_ptr<Y> const& r);
template<class Y>
explicit shared_ptr(weak_ptr<Y> const& r);
template<class Y>
explicit shared_ptr(auto_ptr<Y>& r); //为什么这里不要const? 因为当你复制一个auto_ptr,它们其实被改动了.
template<class Y>
shared_ptr& operator=(shared_ptr<Y> const& r);
template<class Y>
shared_ptr& operator=(auto_ptr<Y>& r);//为什么这里不要const? 原因同上
...
};
这里还有一点我们要注意:member template并不会改变语言规则.而在前面我们曾提到,如果程序需要一个copy构造函数,你却没有声明它,编译器会为你暗自生成一个.在class内申明泛化copy构造函数(member template)并不会阻止编译器生成它们自己的copy构造函数(non-template),故你想要控制构造的方方面面,你必须同时声明泛化copy构造和普通copy构造.赋值操作也是原因.下面的tr1::shared_ptr的定义摘要,就证明了这点是正确的:
template<class T>
class shared_ptr{
public:
shared_ptr(shared_ptr const& r); //copy 构造函数
template<class Y>
shared_ptr(shared_ptr<Y> const& r); //泛化的copy构造
shared_ptr& operator=(shared_ptr const& r); //copy assignment
template<class Y>
shared_ptr& operator=(shared_ptr<Y> const& r); //泛化copy assignment
...
};
请记住:
■ 请使用member function template(成员函数模板)生成"可几首所有兼容类型"的函数
■ 如果你声明member template用于"泛化copy构造"或"泛化assignment操作",你还需要声明正常copy构造函数和copy assignment操作符.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: