首先说下标准库的swap算法:

 namespace std{
template<typename T>
void swap(T & a, T & b)
{
T tmp = a;
a = b;
b = tmp;
}
}

显然的,标准库实在是索然无味,自己随便写都能写出来,而且这样写有时候对于某些来说效率是特别低的。看看下面这个这个例子:

 class WidgetImpl{
public:
...
private:
int a, b, c;
std::vector<double> v;
...
};
class Widget{
pubcic:
Widget(const Widget & rhs);
Widget & operator=(const Widget & rhs)
{
...
*pImpl = *(rhs.pImpl);
...
}
private:
WidgteImpl * pImpl;
};
这个operator=配上上面那个swap一起的效率便非常的地下,本来交换Widget中Impl两者的指针就可以很高效的完成swap操作,但是这里硬是活生生的拷贝了两个对象。
那么怎么完成高效的swap函数呢,首先想到的可能就是将标准库中的swap特例化,像下面这样:
 namespace std{
template<>
void swap<Widget>(Widget & a, Widget & b)
{
swap(a.Impl, b.Impl);
}
}

上面这个swap是标准库swap的total template specialization版本,但是他并不能通过编译,原因是因为其访问了对象的私有函数。正确的做法一般是下面这样:

 class Widget{
public:
...
void swap(Widget & other){
using std::swap;
swap(pUmpl, other.pUmpl);
}
...
};
namespace std{
template<>
void swap<Widget>(Widget & a, Widget & b)
{
a.swap(b);
}
}
上面的做法与STL标准库也是高度一致,标准库的容器也都是有std版本的swap而且都在自己内部空间实现了swap供给前者调用。
但是上述的做法在Widget是类模板的时候就不起作用了。因为下面的式子:
namespace std{
template<typename T>
void swap<Widget<T> >(Widget<T> & a, Widget<T> & b)
{
a.swap(b);
}
}
被c++规定为非法的。
所以所,在无法进行模板特例化的情况下,就需要在特定的自己定义的名字空间创造一个swap函数(或者在std空间中重载一个版本,但是c++是不允许那样做的)。那么据类似下面这样:
 namespace WidgetStuff{
...
template<typename T>
class Widget{.......};
...
template <typename T>
void swap(Widget <T> & a, Widget<T> & b)
{
a.swap(b);
}
}
这样就大功告成了。。
这样,在任何的位置调用有关Widget的swap的时候,就会首先在WidgetStuff的名字空间里面寻找相应的swap。
注意Widget的成员函数版本的swap中有一行是加上了下划线的,这个using声明语句的作用是即使当这个名字空间中的swap对Impl不能起到作用的时候,至少可以让Impl可以借助于std::swap来完成交换的操作。
 
总结一下:
如果因为系统默认版本的swap效率不足话,那么就要做下面这几件事:
    1. 提供一个class public swap对象,使其高效的置换两个特殊对象的值
    2. 在这个class或者template所在的命名空间提供一个non-memberswap,作用是来调用上述的
    swap.
    3.如果上述的class或者class template实际上是个class而非template,最好是特例化std::swap
    ,并且用这个来调用member的swap函数。
    4.在使用swap的时候,尤其是在自己定义的命名空间下,记得使用一个using 声明吧std::swap包含进来,这样即使在本命名空间中找不到合适的swap,至少也可以调用std的swap.且使用swap的时候不要再前面加上名字空间声明,这样前面声明的std::swap就白做了
 
注意,成员版本的swap绝对不能抛出异常,但是非成员版本的swap函数是允许抛出异常的。前者不抛出异常的原因是其往往是针对内置类型的操作,而内置类型的操绝对不允许抛出异常。
 

条款25:考虑写出一个不抛出异常的swap函数的更多相关文章

  1. 读书笔记_Effective_C++_条款二十五: 考虑写出一个不抛出异常的swap函数

    在之前的理论上调用对象的operator=是这样做的 void swap(A& x) { std::swap(a, x.a); } A& operator=(const A& ...

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

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

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

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

  4. 考虑写一个不抛出异常的swap函数

    我们可以调用std下的swap函数,这是一个模板函数:既可以: ; ; std::swap(a,b); cout<<"a = "<<a<<&qu ...

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

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

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

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

  7. 如何写出一个让人很难发现的bug?

    程序员的日常三件事:写bug.改bug.背锅.连程序员都自我调侃道,为什么每天都在加班?因为我的眼里常含bug. 那么如何写出一个让(坑)人(王)很(之)难(王)发现的bug呢? - 1 -新手开发+ ...

  8. 浅谈如何写出一个让(坑)人(王)很(之)难(王)发现的bug

    该文章内容来自脚本之家,原文链接:https://www.jb51.net/news/598404.html 程序员的日常三件事:写bug.改bug.背锅.连程序员都自我调侃道,为什么每天都在加班?因 ...

  9. 请写出一个超链接,点击链接后可以向zhangsan@d-heaven.com发送电子邮件。

    请写出一个超链接,点击链接后可以向zhangsan@d-heaven.com发送电子邮件. <a href=”mailto: zhangsan@d-heaven.com”>发邮件</ ...

随机推荐

  1. windows下的Mysql安装与基本使用(zip)

    一.安装过程 Mysql社区版下载地址:http://dev.mysql.com/downloads/mysql/ --注意,已管理员身份运行cmd.exe,很重要!!目录在,c:\windows\s ...

  2. redis的一些命令

    字符串操作 EX在设置值的时候设置过期时间,ttl查看过期时间 expire能单独设置过期时间 查看所有的key key * 列表操作 lpush从列表左边添加值,rpush从列表右边添加值 lran ...

  3. java线程小结1

    1.创建线程的两种方法 新线程的创建和启动都是通过java代码触发的.除了第一个线程(也就是启动程序的.运行main()方法的线程)是由java平台直接创建的之外,其余的线程都是在java代码中通过“ ...

  4. http post url参数封装(key token 及校验码)

    post请求本来是一种很常见的web请求方式,相信许多项目都有一系列的封装工具类. 今天遇着一个特殊的需求. 需要在post的请求url内封装相应的token 与及key相关的值,这就奇怪了,url封 ...

  5. responseEntity 实现文件下载

    @RequestMapping("/testResponseEntity") public ResponseEntity<byte[]> testResponseEnt ...

  6. java GC 回收机制 转

    JVM分代垃圾回收策略的基础概念 由于不同对象的生命周期不一样,因此在JVM的垃圾回收策略中有分代这一策略.本文介绍了分代策略的目标,如何分代,以及垃圾回收的触发因素. 文章总结了JVM垃圾回收策略为 ...

  7. linux 基础知识总结2

    1. 设定文件text的属性为:文件属主(u) 增加写权限;与文件属主同组用户(g) 增加写权限;其他用户(o) 删除执行权限:chmod ug+w,o-x log2012.log     权限选择参 ...

  8. Oracle---------coalesce的用法介绍

    COALESCE (expression_1, expression_2, ...,expression_n)依次参考各参数表达式,遇到非null值即停止并返回该值.如果所有的表达式都是空值,最终将返 ...

  9. try-catch-finally的问题

    参考: https://blog.csdn.net/chengzhezhijian/article/details/17264531 面试一家公司的面试题,注: 那个面试官对这个问题挺看重的(可是我回 ...

  10. 20145201《Java程序设计》第十周学习总结

    教材学习内容总结 网络编程 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据. 程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这个就是狭义的网络编程范畴. 在发送和接收 ...