《Effective C++》学习笔记(条款45:运用成员函数模板接受所有兼容类型)

最近开始看《Effective C++》,为了方便以后回顾,特意做了笔记。若本人对书中的知识点理解有误的话,望请指正!!!

智能指针是”行为像指针“的对象,并提供指针没有的机能。

真实指针做得很好得一件事是:支持隐式转换。如派生类指针隐式转换为基类指针,”指向 非常量对象“的指针可以转换为”指向 常量对象“的指针等到。如:

class Top  { ... };
class Middle: public Top  { ... };
class Bottom: public Middle  { ... };

Top* pt1 = new Middle;    				// 将Middle* 转换为 Top*
Top* pt2 = new Bottom;    				// 将Bottom* 转换为 Top*
const Top* pt2 = pt1;     				// 将Top* 转换为const Top*

如果想在用户自定的智能指针中模拟上述转换,稍稍有点麻烦。我们希望以下代码通过编译:

template<typename T>
class SmartPtr {
public:
    explicit SmartPtr(T* realPtr);    // 智能指针通常以内置指针完成初始化
    ...
};
SmartPtr<Top> pt1 = SmartPtr<Middle>(new Middle);    	// 将Middle转换为Top
SmartPtr<Top) pt2 = SmartPtr<Bottom>(new Bottom);    	// 将Bottom转换为Top
SmartPtr<const Top> pct2 = pt1;    						// 将Top转换为const Top

但是,同一个 模板 的不同具现体之间并不存在什么与生俱来的固有关系。(指如果以带有base-derived关系的B、D两类型分别具现化某个模板 ,产生出来的两个具现体并不带有base-derived关系),所以编译器视 SmartPtr<Middle>SmartPtr<Top> 为完全不同的类,为了实现 SmartPtr之间的转换,必须将它们明确地写出来。

1 模板和泛型编程(Generic Programming)

我们应该关注如何编写智能指针的构造函数,使其行为能够满足我们的转型需要。但是,我们永远无法写出我们需要的所有构造函数。在上述的继承体系中,我们可以根据一个 SmartPtr<Middle>SmartPtr<Bottom>构造出一个SmartPtr<Top> ,但这个继承体系在未来某天会扩充呢,SmartPtr<Top> 对象又得根据其它智能指针构造自己。

我们永远无法写出我们需要的所有构造函数。因为一个 模板可被无限量具现化,以致生成无限量的函数。因此,似乎我们需要的不是为SmartPtr 写一个构造函数,而是为它写一个构造模板。这样的模板是所谓 成员函数模板(member function templates),其作用是为类生成函数:

template<typename T>
class SmartPtr {
public:
    template<typename U>					// 成员函数模板
    SmartPtr(const SmartPtr<U>& other);    	// 生成copy构造函数
    ...
};

上述代码的意思是:对任何类型 T 和任何类型 U,这里可以根据 SmartPtr<U> 生成一个 SmartPtr<T> —— 因为 SmartPtr<T> 有个构造函数接受一个 SmartPtr<U> 参数。这一类构造函数根据对象 u 创建对象 t(例如根据 SmartPtr<U> 创建一个 SmartPtr<T>),而 u 和 v 的类型是一个模板的不同具现体,有时我们称为泛化拷贝构造函数

上述中的 泛化拷贝构造函数 省去 explicit 的原因:原始指针类型之间的转换是隐式转换,无需明白写出转型动作,智能指针模仿这种行为也合理。

我们希望根据一个 SmartPtr<Bottom> 创建一个 SmartPtr<Top>,却不希望根据一个 SmartPtr<Top> 创建一个 SmartPtr<Bottom>,因为那对 public继承 而言是矛盾的。我们也不希望根据一个 SmartPtr<double> 创建一个 SmartPtr<int>,因为现实中并没有“将int* 转换为 double* ”的对应隐式转换行为。所以,必须从某方面对这一 成员函数模板 所创建的成员函数群进行拣选或筛除。

假如 SmartPtr 提供一个 get 成员函数返回智能指针所持有的那个原始指针的副本,可以通过在”构造模板”实现代码中约束转换行为:

template<typename T>
class SmartPtr  {
public:
    template<typename U>
    SmartPtr(const SmartPtr<U>& other)  	// 以other的heldPtr
    	:heldPtr(other.get()) { ... }    	// 初始化this的heldPtr
    
    T* get() const  { return heldPtr; }    
    ...
private:
    T* heldPtr;    							// 这个SmartPtr持有的内置指针
};

使用 成员初始化列表 来初始化 SmartPtr<T> 之内类型为 T* 的成员变量,并以类型为 U* 指针(由SmartPtr<U> 持有)作为初值。
这个行为只有当”存在某个隐式转换可将一个U 指针转为一个T 指针**”时才能通过编译。最终的效益是 SmartPtr<T> 现在有了一个泛化拷贝构造函数,这个构造函数只在其所获得的实参隶属适当类型时才通过编译。

2 成员函数模板 的赋值操作

成员函数模板 的效用不限于构造函数,它还支持赋值操作。TR1的 shared_ptr 支持所有”来自兼容的内置指针、tr1::shared_ptrauto_ptrtr1::weak_ptr ”的构造行为,以及所有来自上述各物(tr1::weak_ptr 除外)的赋值操作。下面是TR1规范中关于tr1::shared_ptr 的一份摘录,其中强烈倾向声明 template参数 时采用关键字class而不采用 typename(条款42说过两者的意义在此语境下完全相同)。

template<class T>
class shared_ptr {
public:
    //构造来自任何兼容的内置指针,或shared_ptr、或weak_ptr、或auto_ptr
    template<class Y> 
    explicit shared_ptr(Y* p);					//内置指针
    
    template<class Y> 
    shared_ptr(shared_ptr<Y> const& r);			//shared_ptr
    
    template<class Y> 
    explicit shared_ptr(weak_ptr<Y> const& r);	//weak_ptr
    
    template<class Y> 
    explicit shared_ptr(auto_ptr<Y>& r);					//auto_ptr
    
    //赋值来自任何兼容的内置指针,或shared_ptr、或auto_ptr
    template<class Y> 
    shared_ptr& operator = (shared_ptr<Y> const& r);
    
    template<class Y> 
    shared_ptr& operator = (auto_ptr<Y>& r);
    ...
};

上述所有构造函数都是 explicit,唯有”泛化拷贝构造函数”除外。它意味从某个 shared_ptr 类型隐式转换至另一个 shared_ptr 类型是被允许的,但从某个内置指针或从其他智能指针类型进行隐式转换则不被认可(如果是显示转换如 cast 强制转型动作倒是可以)。

传递给 tr1::shared_ptr 构造函数和赋值操作符的 auto_ptr 并未声明为 const,与之形成对比的则是 tr1::shared_ptrtr1::weak_ptr 都以 const 传递。这是因为条款13说过,当你复制一个 auto_ptr,它们其实被改动了。

成员函数模板并不改变语言规则,而语言规则说,如果程序需要一个 拷贝构造函数,你却没有声明它,编译器会为你自动生成一个。在类内声明 泛化拷贝构造函数 (是个 member template)并不会阻止编译器生成它们自己的 拷贝构造函数(是个 non-template),所以如果想要控制 拷贝构造 的方方面面,则必须同时声明 泛化拷贝构造函数 和正常的 拷贝构造函数。相同规则也适用于赋值操作。 下面是 tr1::shared_ptr的一份定义摘要,例证上述所言:

template<class T>
class shared_ptr {
public:
    shared_ptr(shared_ptr const& r);					//拷贝构造函数
    
    template<class Y> 
    shared_ptr(shared_ptr<Y> const& r);					//泛化拷贝构造函数

    shared_ptr& operator = (shared_ptr const& r);		//拷贝构造赋值函数
    
    template<class Y> 
    shared_ptr& operator = (shared_ptr<Y> const& r);	//拷贝构造赋值函数
    ...
};

Note:

  • 请使用 成员函数模板 生成”可接受所有兼容类型”的函数
  • 如果你声明 函数模板 用于”泛化拷贝构造” 或 “泛化赋值操作”, 你还是需要声明正常的 拷贝构造函数 和 拷贝赋值操作符。

条款46:需要类型转换时请为模板定义非成员函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值