首先说下标准库的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. tomcat 6 利用ExpiresFilter控制静态文件缓存

    在tomcat7下面 利用ExpiresFilter来控制静态文件缓存很方便,按照tomcat官网手动配置即可: 但是tomcat6 里面并没有 org.apache.catalina.filters ...

  2. HDU2571:命运(简单dp)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=2571 没什么好说的,不过要处理好边界. 代码如下: #include <iostream> # ...

  3. vue中的锚点和查询字符串

    1.什么是锚点 锚点(achor):其实就是超链接的一种. 如:普通的超链接 <a href="sub_task_detail.php">详细</a>  主 ...

  4. Bytes to be written to the stream exceed the Content-Length bytes size specified 解决方法

    context.Response.ContentType = encode;                using (StreamWriter writer = new StreamWriter( ...

  5. Codeforces Round #397 by Kaspersky Lab and Barcelona Bootcamp (Div. 1 + Div. 2 combined) B - Code obfuscation

    地址:http://codeforces.com/contest/765/problem/B 题目: B. Code obfuscation time limit per test 2 seconds ...

  6. cdoj1339郭大侠与线上游戏

    地址:http://acm.uestc.edu.cn/#/problem/show/1339 题目: 郭大侠与线上游戏 Time Limit: 6000/2000MS (Java/Others)    ...

  7. SVN使用—高级用法

    一.SVN分支 Branch 选项会给开发者创建出另外一条线路.当有人希望开发进程分开成两条不同的线路时,这个选项会非常有用. 情景: 比如项目 demo 下有两个小组,svn 下有一个 trunk ...

  8. H5中的语义化标签

    H5中的语义化标签也就是之前的id = “header”演变而来的 只不过之前是id 现在变成了标签而已 什么是语义化: 根据内容结构化(内容语义化) 选择合适的标签(代码语义化) 便于开发者阅读和写 ...

  9. JavaWeb Listener

    1. 监听器概述 1.1. 什么是监听器 做过Swing或者AWT图像界面Java程序开发的话,应该对Listener与Event非常熟悉.Swing或者AWT中通过Listener与Event来处理 ...

  10. java基础(2)--进制

    进制 进制基础, 目的:理解计算机只能处理2进制的数据和指令 1)10进制计数规律 数字: 0 1 2 3 4 5 6 7 8 9 基数:10 权:  1000 100 10 1 权是基数的n次幂 2 ...