条款25:考虑写出一个不抛出异常的swap函数
来源:互联网 发布:淘宝关闭蚂蚁花呗支付 编辑:程序博客网 时间:2024/04/29 19:12
swap原本只是STL的一部分,后面成为异常安全编程的脊柱,及处理自我赋值安全性的一个常见机制。例子:标准程序库提供的swap算法namespace s td{ template<typenameT>void swap(T& a,T& b) //std::swap的典型实现{T tmp(a);a=b;b=tmp;}}要求:类型T支持copying(通过copying构造函数和copyassignment操作符完成)
上述代码主要涉及三个对象的复制,但对于某些类型而言,复制并不重要。
如pimpl手法(point to implementation)”以指针指向一个对象,内含真正数据”class WidgetImpl //针对widget设计的类{ public: ... private: //数据多,复制时间长 inta,b,c; std::vector<double>v; ...}; class Widget //采用pimpl手法{ public: Widget(constWidget& rhs); Widget&operator=(const Widget& rhs) //复制Widget时 令其复制WidgetImpl对象 { ... *Impl=*(rhs.Impl); ... } private: WidgetImpl*Impl; //指针,内含Widget数据};
转换述对象,只需置换pImpl指针,相对比复制对象效率更高。
为了让std::swap知道置换Widget时是置换指针pImpl,可行的方法是将std::swap针对Widg特化,如下构想。namespace std { template<> voidswap<Widget>(Widget& a,Widget& b) //std::swap针对“T是Widget”的特化版本 { swap(a.Impl,b.Impl); //很明显不能编译通过,因为没有访问private的权限 } }
“template<>”表明它是std::swap的一个全特化版本(total template specialization),函数名后的<Widget>表示这一特化版本是针对“T是Widget”而设计。(一般性的swap施行于Widget便会启用这个版本)
通常不允许改变std命名空间内的任何东西,但允许为标准的templates(如swap)制造特化版本,使它专属于某个类(如Widget) 上述不编译不能过,我们可以置其为友员。还有解决方法是:令Widget声明一个名为swap的public成员函数做真正的置换工作。然后将std::swap特化,令它调用该成员函数,如下:class Widget //同前,唯一差别是增加了swap{ ... voidswap(Widget& other) { uingstd::swap; //如果没有专属swap,就使用std内的swap,(这就是函数内声明std::swap的原因) swap(Impl,other.Impl); //只需交换指针 } ...};namespace std { template<> voidswap(Widget& a,Widget& b) //修订后的std::swap特化版本 { a.swap(b); //置换Widget,调用其成员函数 } }
这种作法能通过编译,还与STL有一至性:因为所有STL容器提供有public swap成员函数和std::swap的特化版本。
当是类模版而非类时,如下代码:template<typename T> classWidgetImpl{...}; template<typename T> classwidget{...};namespace std { template<typenameT> voidswap<Widget<T>>(Widget<T>& a,Widget<T>& b) //错误,不合法 { a.swap(b); } }
看似合理,实则错误。因为上面我们企图偏特化(partially specialize)一个函数模板(function template(std::swap)),C++仅允许对类模板偏特化。
当偏化一个函数模版时,惯常做法是简单的函数重载,如下代码,但是也是不合法的、namespace std //std::swap的一个重载版本 { template<typenameT> //swap后面不跟“< >” voidswap (Widget<T>& a,Widget<T>& b) //错误,不合法 { a.swap(b); } }
一般重载函数模版没问题,但std是特殊的命名空间。标准委员会禁止膨胀那些已经声明好的东西。也就是说:我们可以全特化std内的template,但不能添加任何新的东西(模版或class或functions等)。
解决方法:声明一个non-member的swap调用member swap,但不再将non-member声明为std::swap的特化或重载版本。假设所有widger机能位于名字空间WidgetStuff内,如下代码:
namespaceWidgetStuff { ... //WidgerImpl类同前 classWidget{...}; //类含swap成员函数 template<typenameT> voidswap<Widget<T>>(Widget<T>& a,Widget<T>& b) //非成员函数,但不属于std空间 { a.swap(b); } }
现在任何地点置换两个widget对象,C++会根据名称查找法则(name lookuprule)找到WidgetStuff内专属版本。
如果想让“class专属版”swap在更多情况下适用,应在该class所在命名空间内写一个non-menber版本和std::swap特化版本
对于default swap,member swap, non-member swaps,std::swap特化版本做个总结:如果默认swap效率好,则使用缺省swap,反之,则、1、提供一个public成员函数,高效转换对象值。(但这个函数绝不能抛出异常)2、如果提供一个member swap,也应该提供一个non-memberswap来调用前者。对class(而非类模版),特化std::swap。3、调用swap时应针对std::swap使用using 声明式,然后调用swap不带任何“命名空间资格修饰符”。4、对“用户定义类型”进行stdtemplate全特化。但不能在std内加入全新东西。
0 0
- 第四部分(4)(条款25:考虑写出一个不抛出异常的swap函数)
- 条款25:考虑写出一个不抛出异常的swap函数
- Effective c++(第三版) 条款25:考虑写出一个不抛出异常的swap函数
- 《Effective C ++ 》资源管理:条款25--考虑写出一个不抛出异常的swap函数
- 《Effect C++》学习------条款25:考虑写出一个不抛出异常的swap函数
- [effectiv c++]条款25:考虑写出一个不抛出异常的swap函数
- 考虑写出一个不抛出异常的swap函数
- 考虑写出一个不抛出异常的swap函数
- 条款25:考虑写一个不抛出异常的swap函数
- Effective C++——》条款25:考虑写一个不抛出异常的swap函数
- 条款25:考虑写一个不抛出异常的swap函数
- 25考虑写出一个不抛出异常的swap函数——effective c++学习笔记
- C++之考虑写出一个不抛出异常的swap函数(25)---《Effective C++》
- 条款25:考虑写出一个不抛异常的swap函数
- 条款25:考虑写出一个不抛异常的swap函数
- 条款25:考虑写出一个不抛异常的swap函数
- 《Effective C++》学习笔记条款25 考虑写出一个不抛异常的swap函数
- 条款25:考虑写出一个不抛异常的swap函数
- 职场菜鸟如何更好的提升自己?
- 全志行车记录仪里面FireEyepublic.apk反编译的分析(一)
- Unity亚洲开发者大会会议简录之技术篇,Profiler的一些参数的意思
- 如何用python读取文本中指定行的内容
- 1.ng-bind的使用
- 条款25:考虑写出一个不抛出异常的swap函数
- 统计套利六--交易量&回归模型
- Spring AOP使用限制
- java深入浅出设计模式----简单的工厂模式(一)----初学者容易犯的错误(一)
- C程序代码中内嵌as汇编(一、基础知识)
- POJ 1671 Rhyme Schemes(第二类string数)
- linux 查询 java 日志输出两份日志
- centos 挂载 ntfs 移动硬盘
- 深入云存储系统Swift存储节点:存储实现分析