[Effective C++ --025]考虑写出一个不抛异常的swap函数
引言
在我的上一篇博客中,讲述了swap函数。
原本swap只是STL的一部分,而后成为异常安全性编程的脊柱,以及用来处理自我赋值可能性。
一、swap函数
标准库的swap函数如下:
- namespace std {
- template<typename T>
- void swap(T &a, T& b)
- {
- T temp(a);
- a = b;
- b = temp;
- }
- }
只要类型T支持拷贝(通过拷贝构造函数和拷贝赋值操作符完成),那么这个版本的swap函数就会帮你置换类型为T的对象。
二、Pimpl情境下的swap函数
假设我们有如下的类。
- class WidgetImpl {
- public:
- ...
- private:
- int a,b,c; // 可能有许多数据
- std::vector<double> v; // 意味复制时间很长
- ....
- }
- class Widget {
- public:
- Widget(const Widget& rhs);
- Widget& operator = (const Widget& rhs)
- {
- .....
- *pImpl = *(rhs.pImpl);
- }
- private:
- WidgetImpl* pImpl;
- };
一旦要置换两个Widget对象,我们唯一需要做的就是置换其pImpl指针,但是一种的swap函数不知道这一点,它会复制三个Widget和三个WidgetImpl,效率非常低下!
所以我们希望的效果是:只交换pImpl指针!
因此我们可以设计出这个思路:
- namespace std {
- template<> // 表示它是std::swap的一个特化版本
- void swap<Widget>(Widget &a, Widget& b)
- {
- swap(a.pImpl, b.pImpl);
- }
- }
上述代码当然通不过编译,因为pImpl是私有成员。或许你说我们可以用friend函数来解决这一问题,可是这不大符合预期!
那我们再包装一下:
- class Widget {
- public:
- ...
- void swap(Widget& other)
- {
- using std::swap;
- swap(pImpl, other.pImpl); // 若置换Widgets就置换其pImpl指针
- }
- ....
- }
- namespace std {
- template<> // 表示它是std::swap的一个特化版本
- void swap<Widget>(Widget& a, Widget& b)
- {
- a.swap(b);
- }
- }
这样不仅通过编译,还与STL容器具有一致性,因为所有的STL容器也都提供有public swap成员函数和std::swap特化版本。
但需要注意的是:C++只允许对class templates偏特化,在function template上特化是行不通的。如下面代码是编译不过的。
- namespace std {
- template<typename T>
- void swap<Widget<T>>(Widget<T>& a,
- Widget<T>& b)
- {
- swap(a.pImpl, b.pImpl);
- }
- }
三、function template偏特化
那么,如果我们想在function template上特化,应该怎么做呢?答案是添加一个重载版本!
如下:
- namespace std {
- template<typename T>
- void swap(Widget<T>& a, // 注意这里没有“<Widget<T>>”
- Widget<T>& b)
- {
- swap(a.pImpl, b.pImpl);
- }
- }
同时,为了其他人调用swap时能取得我们提供的较高版本的template特定版本,我们可以声明一个non-member函数!
- namespace Widget {
- template<typename T>
- class Widget{....}
- template<typename T>
- void swap(Widget<T>& a, // 注意这里没有“<Widget<T>>”
- Widget<T>& b)
- {
- swap(a.pImpl, b.pImpl);
- }
- }
◆总结
1.当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。
2.如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于classes,也请特化std::swap。
3.调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。
4.为“用户定义类型”进行std template全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西
[Effective C++ --025]考虑写出一个不抛异常的swap函数的更多相关文章
- Effective C++:条款25:考虑写出一个不抛异常的swap函数
(一) 缺省情况下swap动作可由标准程序库提供的swap算法完毕: namespace std { template<typename T> void swap(T& a, T& ...
- 《Effective C++》item25:考虑写出一个不抛异常的swap函数
std::swap()是个很有用的函数,它可以用来交换两个变量的值,包括用户自定义的类型,只要类型支持copying操作,尤其是在STL中使用的很多,例如: int main(int argc, _T ...
- Effective C++ Item 25 考虑写出一个不抛异常的swap函数
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛 ...
- EC读书笔记系列之13:条款25 考虑写出一个不抛异常的swap函数
记住: ★当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定其不抛出异常 ★若你提供一个member swap,也该提供一个non-member swap来调用前者.对于cla ...
- 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& ...
- 考虑实现一个不抛异常的swap
Effective C++:参考自harttle land 类的swap实现与STL容器是一致的:提供swap成员函数, 并特化std::swap来调用那个成员函数. class Widget { p ...
- 输入一个数字n 如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数 写出一个函数
题目: 输入一个数字n 如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数 写出一个函数 首先,这道题肯定可以用动态规划来解, n为整数时,n的解为 n/2 的解加1 n为奇数时 ...
- java————数组 简单写出一个管理系统
数组的特点 1, 数组是一块连续的空间,下标描述空间的位置. 2, 下标从0开始,最大下标为数组长度—1.(*.length-1) 3, 数组元素都是变量.(就是每个下标对应的内容).变量的类型 ...
随机推荐
- 剑指Offer:二进制中1的个数
题目:输入一个整数,输出该数二进制表示中1的个数. // 二进制中1的个数 #include <stdio.h> int wrong_count_1_bits(int n) // 错误解法 ...
- MySQL InnoDB存储引擎中的锁机制
1.隔离级别 Read Uncommited(RU):这种隔离级别下,事务间完全不隔离,会产生脏读,可以读取未提交的记录,实际情况下不会使用. Read Committed (RC):仅能读取到已提交 ...
- 三相异步电动机过载保护及报警PLC控制
一.项目所需设备.工具.材料 见表7-1. 表7-1 项目所需设备.工具.材料 二. 训练内容: 1.项目描述 试设计一电动机过载保护程序,要求电动机过载时能自动停止运转,同时发出10秒钟的声光报 ...
- wcf的连接数
</standardEndpoints> <behaviors> <serviceBehaviors> <behavior name="thrott ...
- 【暑假】[实用数据结构]前缀树 Trie
前缀树Trie Trie可理解为一个能够快速插入与查询的集合,无论是插入还是查询所需时间都为O(m) 模板如下: +; ; struct Trie{ int ch[maxnode][sigma_siz ...
- 【Hadoop代码笔记】Hadoop作业提交之客户端作业提交
1. 概要描述仅仅描述向Hadoop提交作业的第一步,即调用Jobclient的submitJob方法,向Hadoop提交作业. 2. 详细描述Jobclient使用内置的JobS ...
- 【转】#include_next <filename.h>
转载自 http://bbs.csdn.net/topics/390381450 #include_next仅用于特殊的场合. 它被用于头文件中(#include既可用于头文件中, 又可用于.c文件中 ...
- 教程-Python实例-发送邮件功能
相关资料: http://www.cnblogs.com/xiaowuyi 实例代码: import smtplib from email.mime.text import MIMEText mail ...
- 问题-关于SizeOf在Delphi7和Delphi2009下结果分别是16/32
问题:同样的代码在Delphi7和Delphi2009下结果分别是16/32,为什么?var LWindCode : array [0..15] of char; begin showmess ...
- altium designer 里如何设置PCB默认字符默认大小(PCB丝印)
注意:此操作只对新导入的元件生效.