引言

在我的上一篇博客中,讲述了swap函数。

原本swap只是STL的一部分,而后成为异常安全性编程的脊柱,以及用来处理自我赋值可能性。

一、swap函数

标准库的swap函数如下:

  1. namespace std {
  2. template<typename T>
  3. void swap(T &a, T& b)
  4. {
  5. T temp(a);
  6. a = b;
  7. b = temp;
  8. }
  9. }

只要类型T支持拷贝(通过拷贝构造函数和拷贝赋值操作符完成),那么这个版本的swap函数就会帮你置换类型为T的对象。

二、Pimpl情境下的swap函数

假设我们有如下的类。

  1. class WidgetImpl {
  2. public:
  3. ...
  4. private:
  5. int a,b,c; // 可能有许多数据
  6. std::vector<double> v; // 意味复制时间很长
  7. ....
  8. }
  9.  
  10. class Widget {
  11. public:
  12. Widget(const Widget& rhs);
  13. Widget& operator = (const Widget& rhs)
  14. {
  15. .....
  16. *pImpl = *(rhs.pImpl);
  17. }
  18. private
  19. WidgetImpl* pImpl;
  20. };

一旦要置换两个Widget对象,我们唯一需要做的就是置换其pImpl指针,但是一种的swap函数不知道这一点,它会复制三个Widget和三个WidgetImpl,效率非常低下!

所以我们希望的效果是:只交换pImpl指针!

因此我们可以设计出这个思路:

  1. namespace std {
  2. template<> // 表示它是std::swap的一个特化版本
  3. void swap<Widget>(Widget &a, Widget& b)
  4. {
  5. swap(a.pImpl, b.pImpl);
  6. }
  7. }

上述代码当然通不过编译,因为pImpl是私有成员。或许你说我们可以用friend函数来解决这一问题,可是这不大符合预期!

那我们再包装一下:

  1. class Widget {
  2. public:
  3. ...
  4. void swap(Widget& other)
  5. {
  6. using std::swap;
  7. swap(pImpl, other.pImpl); // 若置换Widgets就置换其pImpl指针
  8. }
  9. ....
  10. }
  11.  
  12. namespace std {
  13. template<> // 表示它是std::swap的一个特化版本
  14. void swap<Widget>(Widget& a, Widget& b)
  15. {
  16. a.swap(b);
  17. }
  18. }

这样不仅通过编译,还与STL容器具有一致性,因为所有的STL容器也都提供有public swap成员函数和std::swap特化版本。

但需要注意的是:C++只允许对class templates偏特化,在function template上特化是行不通的。如下面代码是编译不过的。

  1. namespace std {
  2. template<typename T>
  3. void swap<Widget<T>>(Widget<T>& a,
  4. Widget<T>& b)
  5. {
  6. swap(a.pImpl, b.pImpl);
  7. }
  8. }

三、function template偏特化

那么,如果我们想在function template上特化,应该怎么做呢?答案是添加一个重载版本!

如下:

  1. namespace std {
  2. template<typename T>
  3. void swap(Widget<T>& a, // 注意这里没有“<Widget<T>>”
  4. Widget<T>& b)
  5. {
  6. swap(a.pImpl, b.pImpl);
  7. }
  8. }

同时,为了其他人调用swap时能取得我们提供的较高版本的template特定版本,我们可以声明一个non-member函数!

  1. namespace Widget {
  2. template<typename T>
  3. class Widget{....}
  4.  
  5. template<typename T>
  6. void swap(Widget<T>& a, // 注意这里没有“<Widget<T>>”
  7. Widget<T>& b)
  8. {
  9. swap(a.pImpl, b.pImpl);
  10. }
  11. }

◆总结

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函数的更多相关文章

  1. Effective C++:条款25:考虑写出一个不抛异常的swap函数

    (一) 缺省情况下swap动作可由标准程序库提供的swap算法完毕: namespace std { template<typename T> void swap(T& a, T& ...

  2. 《Effective C++》item25:考虑写出一个不抛异常的swap函数

    std::swap()是个很有用的函数,它可以用来交换两个变量的值,包括用户自定义的类型,只要类型支持copying操作,尤其是在STL中使用的很多,例如: int main(int argc, _T ...

  3. Effective C++ Item 25 考虑写出一个不抛异常的swap函数

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛 ...

  4. EC读书笔记系列之13:条款25 考虑写出一个不抛异常的swap函数

    记住: ★当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定其不抛出异常 ★若你提供一个member swap,也该提供一个non-member swap来调用前者.对于cla ...

  5. Effective C++ -----条款25:考虑写出一个不抛异常的swap函数

    当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者.对于cla ...

  6. 【25】考虑写出一个不抛异常的swap函数

    1.swap交换对象值,std提供了实现方法模版的实现,它的实现是交换对象值. namespace std { template<typename T> void swap(T& ...

  7. 考虑实现一个不抛异常的swap

    Effective C++:参考自harttle land 类的swap实现与STL容器是一致的:提供swap成员函数, 并特化std::swap来调用那个成员函数. class Widget { p ...

  8. 输入一个数字n 如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数 写出一个函数

    题目: 输入一个数字n  如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数  写出一个函数 首先,这道题肯定可以用动态规划来解, n为整数时,n的解为 n/2 的解加1 n为奇数时 ...

  9. java————数组 简单写出一个管理系统

    数组的特点 1,  数组是一块连续的空间,下标描述空间的位置. 2,  下标从0开始,最大下标为数组长度—1.(*.length-1) 3,  数组元素都是变量.(就是每个下标对应的内容).变量的类型 ...

随机推荐

  1. 剑指Offer:二进制中1的个数

    题目:输入一个整数,输出该数二进制表示中1的个数. // 二进制中1的个数 #include <stdio.h> int wrong_count_1_bits(int n) // 错误解法 ...

  2. MySQL InnoDB存储引擎中的锁机制

    1.隔离级别 Read Uncommited(RU):这种隔离级别下,事务间完全不隔离,会产生脏读,可以读取未提交的记录,实际情况下不会使用. Read Committed (RC):仅能读取到已提交 ...

  3. 三相异步电动机过载保护及报警PLC控制

    一.项目所需设备.工具.材料 见表7-1. 表7-1  项目所需设备.工具.材料 二.  训练内容: 1.项目描述 试设计一电动机过载保护程序,要求电动机过载时能自动停止运转,同时发出10秒钟的声光报 ...

  4. wcf的连接数

    </standardEndpoints> <behaviors> <serviceBehaviors> <behavior name="thrott ...

  5. 【暑假】[实用数据结构]前缀树 Trie

    前缀树Trie Trie可理解为一个能够快速插入与查询的集合,无论是插入还是查询所需时间都为O(m) 模板如下: +; ; struct Trie{ int ch[maxnode][sigma_siz ...

  6. 【Hadoop代码笔记】Hadoop作业提交之客户端作业提交

    1.      概要描述仅仅描述向Hadoop提交作业的第一步,即调用Jobclient的submitJob方法,向Hadoop提交作业. 2.      详细描述Jobclient使用内置的JobS ...

  7. 【转】#include_next <filename.h>

    转载自 http://bbs.csdn.net/topics/390381450 #include_next仅用于特殊的场合. 它被用于头文件中(#include既可用于头文件中, 又可用于.c文件中 ...

  8. 教程-Python实例-发送邮件功能

    相关资料: http://www.cnblogs.com/xiaowuyi 实例代码: import smtplib from email.mime.text import MIMEText mail ...

  9. 问题-关于SizeOf在Delphi7和Delphi2009下结果分别是16/32

    问题:同样的代码在Delphi7和Delphi2009下结果分别是16/32,为什么?var   LWindCode : array [0..15] of char; begin   showmess ...

  10. altium designer 里如何设置PCB默认字符默认大小(PCB丝印)

    注意:此操作只对新导入的元件生效.