《Effective C++》学习笔记——条款45

来源:互联网 发布:淘宝网如何注销账户 编辑:程序博客网 时间:2024/04/30 23:12




七、模板与泛型编程


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


让智能指针隐式转换



智能指针:
它是”行为像指针”的对象,并提供指针没有的机能。
真实指针的优点:
支持隐式转换,比如:

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转换为TopSmartPtr<Top) pt2 = SmartPtr<Bottom>(new Bottom);    // 将Bottom转换为TopSmartPtr<const Top> pct2 = pt1;    // 将Top转换为const Top

但是,同一个template的不同具现体之间并不存在什么与生俱来的固有关系。

  • 固有关系:指如果以带有base-derived关系的B、D两类型分别具现化某个template,产生出来的两个具现体并不带有base-derived关系。

所以编译器将SmartPtr<Middle>SmartPtr<Top> 看做两个完全不同的类,为了实现我们上述的转换能力,必须将它们明确地写出来。



Template和泛型编程



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

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,而u和v的类型是一个template的不同具现体,有时我们称之为泛化copy构造函数。

  • 原始指针类型之间的转换是隐式转换,无需明白写出转型动作。在模板化构造函数中略去explicit就是为了这个目的。

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

通过在”构造模板”实现代码中约束转换行为:

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

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




member function templates(成员函数模板)的赋值操作



TR1的shared_ptr支持所有”来自兼容之内置指针、tr1::shared_ptrs、auto_ptrs 和 tr1::weak_ptrs”的构造行为,以及所有来自上述各物(tr1::weak_ptrs除外)的赋值操作。
下面是TR1规范中关于tr1::shared_ptr的一份摘录,其中强烈倾向声明template参数时采用关键字class而不采用typename。

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> shared_ptr(auto_ptr<Y>& r);    template<class Y> shared_ptr& operator = (shared_ptr<Y> const& r);    template<class Y> shared_ptr& operator = (auto_ptr<Y>& r);    ...};
  1. 上述所有构造函数都是explicit,唯有”泛化copyt构造函数”除外。它代表着从某个shared_ptr类型隐式转换至另一个shared_ptr类型是被允许的,但从某个内置指针或从其他智能指针类型进行隐式转换则不被认可。
  2. 传递给tr1::shared_ptr构造函数和assignment操作符的auto_ptrs并未声明为const,与之形成对比的则是tr1::shared_ptrs和tr1::weak_ptrs都以const传递。这是因为条款13说过,当你复制一个auto_ptrs,它们其实被改动了。
  3. member templates并不改变语言规则,而语言规则说,如果程序需要一个copy构造函数,你却没有声明它,编译器会为你自动生成一个。在class内声明泛化copy构造函数并不会阻止编译器生成它们自己的copy构造函数,所以如果想要控制copy构造的所有,则必须同时声明泛化copy构造函数和正常的copy构造函数。相同规则也适用于赋值操作。




请记住

  • 请使用 member function templates 生成”可接受所有兼容类型”的函数。
  • 如果声明 function templates 用于”泛化copy构造” 或 “泛化 assignment操作”, 还是需要声明正常的copy构造函数和copy assignment操作符。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 唐氏筛查神经管缺陷高风险怎么办 门诊处方笺丢了怎么办 孕中期睡觉手麻怎么办 怀孕2个月了没胎心胎芽怎么办 怀孕腿疼的厉害怎么办 孕妇老是失眠多梦怎么办 孕妇会失眠多梦怎么办 怀孕5个月睡不着怎么办 6个月孕妇失眠怎么办 彩超脉络丛囊肿怎么办 双侧脉络丛囊肿怎么办 唐筛神经管缺陷高风险怎么办 雌激素低怎么办吃什么东西补 我怀了狗的孩子怎么办 结婚2年不要孩子怎么办 备孕一直没怀孕怎么办 刚生的婴儿打嗝怎么办 小孩40天黄疸高怎么办 婴儿身高长得慢怎么办 四个月的宝宝哭怎么办 孕39周羊水偏多怎么办 孕39周羊水浑浊怎么办 孕晚期羊水过少怎么办 怀孕脐带绕颈一周怎么办 nt检查宝宝趴着怎么办 四维胎儿有问题怎么办 怀孕70天没有胎心怎么办 怀孕20天不想要怎么办 换轮胎胎压监测怎么办 怀孕了吐的厉害该怎么办 怀孕吐完嗓子疼怎么办 怀孕16周不想要怎么办 怀孕四个月胎盘低置怎么办 孕37周胎盘三级怎么办 孕37周胎盘老化怎么办 怀孕22周胎盘1级怎么办 婴儿吃奶粉吐奶怎么办 宝宝吐奶又拉稀怎么办 羊水穿刺21三体怎么办 开始长妊娠纹了怎么办 已经长了妊娠纹怎么办