EC读书笔记系列之13:条款25 考虑写出一个不抛异常的swap函数
记住:
★当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定其不抛出异常
★若你提供一个member swap,也该提供一个non-member swap来调用前者。对于classes(而非templates),也请特化std::swap
★调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”
★为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西
--------------------------------------------------------------------------------------------
swap函数的背景:
原本其只是STL的一部分,而后成为异常安全性编程的脊柱,以及用来处理自我赋值(Copy-and-swap技术)可能性的一个常见机制。
举例:
class Widget {
public:
Widget( const Widget &rhs );
Widget& operator=( const Widget &rhs ) {
//复制Widget时令其复制其WidgetImpl对象
...
*pImpl = *( ths.pImpl );
...
}
...
private:
WidgetImpl *pImpl;
};
其中的WidgetImpl类如下:
class WidgetImpl {
public:
...
private:
int a, b, c;
std::vector<double> v; //可能有许多数据,意味着复制时间很长
};
若要置换两Widget对象值,仅需置换其pImpl指针,但STL缺省的swap并不知这样操作,它不只复制三个Widget,还复制三个WidgetImpl对象,非常缺乏效率!!!
做法一(编译出错):
namespace std {
template<> //这是std::swap针对T是Widget的特化版
void swap<Widget>( Widget &a, Widget &b ) {
swap( a.pImpl, b.pImpl ); //仅需置换指针就好
}
}
template<>表示它是std::swap的一个全特化版(不能够改变std命名空间内的任何东西,但可以为标准templates制造特化版,使他专属于自己的classes,如本例的Widget,上面的代码就是这样操作的)。上述代码有问题,因为pImpl是private,∴swap( a.pImpl, b.pImpl );语句出错
做法二(针对的是class的情况):
class Widget {
public:
...
void swap( Widget &other ) { //在Widget内增加swap成员函数
using std::swap; //此声明必要
swap( pImpl, other.pImpl ); //置换的是指针
}
...
};
namespace std {
template<> //修订后的std::swap特化版本
void swap<Widget>( Widget &a, Widget &b ) {
a.swap(b); //若要置换Widget,调用其swap成员函数
}
}
此做法可以,而且还与STL容器有一致性,∵所有STL容器也都提供有public swap成员函数和std::swap特化版本(用来调用前者)。
做法三(编译出错):
上述做法是可以,但针对的是Widget和WidgetImpl是class的情况,假设Widget和WidgetImpl都是class template该如何呢
template<typename T> //现在变为class template了
class WidgetImpl {};
template<typename T> //现在变为class template了
class Widget {};
在Widget内放swap成员跟做法二是一样,但特化std::swap时会遇到问题:
namespace std {
template<typename T> //这是偏特化
void swap< Widget<T> >( Widget<T> &a, Widget<T> &b ) { //错误!
a.swap(b);
}
}
这段所谓的特化代码通不过编译,∵我们企图偏特化一个function template(std::swap),但C++只允许对class template偏特化,不能对function template这么搞。
做法四:做法三的解决办法
声明一个non-mem. swap让它调用mem. swap,但不再将那个non-mem. swap声明为std::swap的特化版。
namespace WidgetStuff {
... //模板化的WidgetImpl等等
template<typename T>
class Widget {...}; //同前,内含swap成员函数
...
template<typename T> //单独写一个non-member !!不要特化了!
void swap( Widget<T> &a, Widget<T> &b ) { //注意这里并不属std空间
a.swap(b);
}
}
总结:
目前已经讨论default swap,member swap,non-member swap,std::swap的特化版本。
首先,若swap的缺省实现码对你的class或class template提供可接受的效率,无需做额外的事情;
其次,若swap缺省实现版效率不足,试着做以下事情:
1. 提供一个public swap成员函数,且该函数绝不该抛出异常;
2. 在你的class或template所在的命名空间内提供一个non-member swap,并令其调用上述swap成员函数;(做法四)
3. 若你正在编写一个class(而非class template),为你的class特化std::swap,并令它调用你的swap成员。(做法二)
最后,若你调用swap,请确定包含一个using声明式,以便让std::swap在你的函数内曝光可见,然后不加任何namespace修饰符,赤裸裸地调用swap。(做法二、四都有!!!)
最后一点:成员版swap(即做法二Widget类中所写的swap函数)绝不可抛出异常!!!
EC读书笔记系列之13:条款25 考虑写出一个不抛异常的swap函数的更多相关文章
- Effective C++ Item 25 考虑写出一个不抛异常的swap函数
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛 ...
- Effective C++:条款25:考虑写出一个不抛异常的swap函数
(一) 缺省情况下swap动作可由标准程序库提供的swap算法完毕: namespace std { template<typename T> void swap(T& a, T& ...
- Effective C++ -----条款25:考虑写出一个不抛异常的swap函数
当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者.对于cla ...
- 【25】考虑写出一个不抛异常的swap函数
1.swap交换对象值,std提供了实现方法模版的实现,它的实现是交换对象值. namespace std { template<typename T> void swap(T& ...
- EC读书笔记系列之17:条款41、42、43、44、45、46
条款41 了解隐式接口与编译器多态 记住: ★classes和templates都支持接口和多态 ★对classes而言接口是显式的(explicit),以函数签名为中心.多态则是通过virtual函 ...
- EC读书笔记系列之16:条款35、36、37、38、39、40
条款35 考虑virtual函数以外的其他选择 记住: ★virtual函数的替代方案包括NVI手法及Strategy模式的多种形式.NVI手法自身是一个特殊形式的Template Method模式 ...
- EC读书笔记系列之19:条款49、50、51、52
条款49 了解new-handler的行为 记住: ★set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用 ★Nothrow new是一个颇为局限的工具,∵其只适用于内存 ...
- EC读书笔记系列之14:条款26、27、28、29、30、31
条款26 尽可能延后变量定义式的出现时间(Lazy evaluation) 记住: ★尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率 ----------------------- ...
- EC读书笔记系列之12:条款22、23、24
条款22 将成员变量声明为private 记住: ★切记将成员变量声明为private.这可赋予客户访问数据的一致性.可细微划分访问控制.允诺约束条件获得保证,并提供class作者以充分的实现弹性. ...
随机推荐
- 开发板怎样开启telnet服务
linux开发板开启telnet服务须要一下几个条件: 1.文件系统支持telnet busybox默认是把telnet和telnetd功能编进去了的,所以这一步一般都省了. 2.挂载devpts 挂 ...
- 【贪心+背包】【HDU2546】【饭卡】
饭卡 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...
- java SecurityManager
---- 众所周知,Java语言具有完善的安全框架,从编程语言,编译器.解释程序到Java虚拟机,都能确保Java系统不被无效的代码或敌对的编译器暗中破坏,基本上,它们保证了Java代码按预定的规则运 ...
- ViewState存储到服务器
把viewstate保存在服务器上 将ViewState持久化保持在服务器端的代码,这样ViewState不占用网络带宽,因此其存取只是服务器的磁盘读取时间.并且它很小,可以说是磁盘随便转一圈就能同时 ...
- z-index解决弹出层遮罩层覆盖子div不能显示输出的问题
// 添加以下代码来进行测试: // ajax 发生错误,就会执行$('body').ajaxError(function(e, xhr, setting, text){ // e - even ...
- Sql遍历数据库
Sql遍历数据库 set nocount on ) ) ) set @str='ad' Declare cur_Depart Cursor For select name,id from syscol ...
- C++ extern "C",C与C++的区别
1. C++保留了一部分过程式语言的特点,因而它可以定义不属于任何类的全局变量和函数.但是,C++毕竟是一种面向对象的程序设计语言,为了支持函数的重载,C++对全局函数的处理方式与C有明显的不同. 2 ...
- C++ vector 实现二维数组时, 在类的头文件中定义时遇到"应输入类型符"的问题?
见下,当我在类的声明文件中定义二维vector时,提示我应输入类型说明符; 但是相同的格式定义,在类中将二维vector修改为在源文件中定义就可以顺利通过,并顺利执行打印 打印结果如下: 望大神来解惑 ...
- Linux下 静态链接库 和 动态链接库
先来说说C/C++编译过程 编译: 检查语句符号定义,将C/C++代码翻译生成中间语言. 链接: 将中间代码整合,生成可执行的二进制代码. 简单的说,库文件都是一种特殊的中间语言文件,静态库还是一种特 ...
- hibernate中先建表还是先建实体类
在实际工作中往往是先建表然后再生成类原因:建好数据库表之后往往要对数据表进行一些优化,比如说建索引,比如说建中间表,比如建视图.如果先建类的话这些优化是无法生成的