我们可以调用std下的swap函数,这是一个模板函数:既可以:

    int a = ;
int b = ;
std::swap(a,b);
cout<<"a = "<<a<<" b = "<<b<<endl;

也可以(前提这个类型支持复制构造函数和赋值构造函数):

class Test
{
public:
Test(int i):val(i){}
int getVal(){return val;}
private:
int val;
}; Test a();
Test b();
std::swap(a,b);
cout<<"a = "<<a.getVal()<<" b = "<<b.getVal()<<endl;
return ;

这个函数是是同通过类似int tmp = a; a = b; b = tmp的方法实现的,所以,如果类中的数据成员较多,这样的交换缺乏效率。
相比之下,“以指针指向一个对象,内含真正的数据”的方法更受欢迎。比如pimpl(pointer to implementation)。然后交换它们的指针。按照这种方法,我们应该这样设计我们的类:

//类的具体实现
class TestImpl
{
public:
TestImpl(int i):ival(i){}
int getVal(){return ival;} private:
int ival;
}; //指针
class Test
{
public:
Test(int i):p(new TestImpl(i)){}
~Test(){delete p;}
Test operator=(const Test rhs)
{
*p = *(rhs.p);
}
int getVal()
{
return this->p->getVal();
}
void swap(Test& other)
{
using std::swap;
swap(p,other.p);
}
private:
TestImpl *p;
};

我们在Test类中,同过调用std::swap完成了指针的交换。为了是得我们的swap更像是std中的swap函数,我们将std中的swap特化:

namespace std
{
template<>
void swap<Test>(Test &a,Test &b)
{
a.swap(b);
}
}

特化版本调用的就是类成员函数中的swap。

但是,如果Test和TestImpl都是类模板

//类的具体实现
template <typename T1>
class TestImpl
{
public:
TestImpl(T1 i):ival(i){}
virtual T1 getVal(){return ival;} private:
T1 ival;
}; //指针
template <typename T1>
class Test
{
public:
Test(T1 i):p(new TestImpl(i)){}
~Test(){delete p;}
Test operator=(const Test rhs)
{
*p = *(rhs.p);
}
T1 getVal()
{
return this->p->getVal();
}
void swap(Test& other)
{
using std::swap;
swap(p,other.p);
}
private:
TestImpl *p;
};

那么我们似乎需要这么改写原先的交换函数:

namespace std
{
template<typename T1>
void swap<Test<T1>>(Test<T1> &a,Test<T1> &b)
{
a.swap(b);
}
}

但这并不合法,因为C++规定,不能偏特化一个函数模版。而这里却将swap的类型特化为了Test<T1> &。

有没有好的办法呢?其实很简单,只要把原来的函数重载就行了。

    template<typename T>
void swap(Test<T>& a, Test<T>& b)
{
a.swap(b);
}

剩下的问题就是吧这个函数放在哪里了?首先,设为全局函数是非常不好的,因为我们很有可能会经常调用swap函数的“平凡”版本。所以放在命名空间中是一 个不错的选择。但是有两点需要注意:1.这个命名空间中也必须包括我们定义的模板类。2.不要放在std空间内。虽然放进去也能使用,但是std是C++ 标准委员会定义的,为了方便别人的使用,咱们还是放在咱们自己定义的空间中吧。

现在的考虑另一种情况:假如你在一个函数模板中需要调用swap函数,该怎么做呢?首先,你希望的情况是:最好调用专属的swap,如果不存在,那么调用std下的swap:

template <typename T>
void doSomething(T& obj1, T& obj2)
{
//其他操作省略
using std::swap;
swap(obj1,obj2);
}

那么,根据名字查找规则,则会通过argument-dependent
look先找出命名空间内的重载的swap,如果找不到,则再调用std内的。这里的using声明的作用只是让这个函数“曝光”,至于用不用,则是另一
码事。但是如果你写成了using std::swap(obj1,obj2);就表明你肯定是要用std下的swap了。

总而言之:
1.虽然std下提供了一个swap函数,但是由于这个函数效率不高,所以我们倾向于在交换类时,通过pimpl技术交换指针。
2.首先,我们需要定义一个public swap函数,在这个函数中调用std下的swap函数完成指针的交换。
3.然后定义一个非成员swap函数,这个函数调用public swap。
4.假如的是类模板,那么,需要重载一个swap函数,然后将这个函数与模板类一起放在一个命名空间中。
5.调用swap时应使用using std::swap;声明。这样对于与重载的swap函数类型不符的函数,会调用std下的swap完成。

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

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

    首先说下标准库的swap算法: namespace std{ template<typename T> void swap(T & a, T & b) { T tmp = ...

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

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

  3. 读书笔记 effective c++ Item 25 实现一个不抛出异常的swap

    1. swap如此重要 Swap是一个非常有趣的函数,最初作为STL的一部分来介绍,它已然变成了异常安全编程的中流砥柱(Item 29),也是在拷贝中应对自我赋值的一种普通机制(Item 11).Sw ...

  4. Python写一个批量生成账号的函数

    批量生成账户信息,产生的账户由@sina.com结尾,长度由用户输入,产生多少条也由用户输入,用户名不能重复,用户名必须由大写字母.小写字母和数字组成. def Users(num,len): # n ...

  5. 写一个dup2功能相同的函数,不能调用 fcntl 函数,并且要有出错处理

    实现的时候用到系统原来的dup函数 // mydup2.c // 2015/08/17 Lucifer Zhang version1.0 // write my own dup2 function / ...

  6. 用JavaScript写一个类似PHP print_r的函数

    PHP print_r的函数很好用,可以用来打印数组.对象等的结构与数据,可惜JavaScript并没有原生提供类似的函数.不过我们可以试着自己来实现这个函数,下面提供一些方法与思路. 方法一 fun ...

  7. 写一个dup2功能同样的函数,不能调用 fcntl 函数,而且要有出错处理

    实现的时候用到系统原来的dup函数 // mydup2.c // 2015/08/17 Lucifer Zhang version1.0 // write my own dup2 function / ...

  8. c++下为使用pimpl方法的类编写高效的swap函数

    swap函数是c++中一个常用的函数,用于交换两对象的值,此外还用于在重载赋值运算符中处理自赋值情况和进行异常安全性编程(见下篇),标准模板库中swap的典型实现如下: namespace stl { ...

  9. Python_代码练习_写一个判断是否为小数的函数

    这两天在学习函数,练习写一个判断是否为小数的函数,看起来蛮简单的,飞速写完很是得意,然后测了一下,发现差得好多呀,这个并不像想象那样简单,我得到的教训是,想要把一个需求哪怕再小的需求考虑周全,都不是件 ...

随机推荐

  1. JSP 插入到数据库的数据出现 “SQLServerException: 将截断字符串或二进制数据” 错误解决方案

    最近在编写一个小型基于的jsp系统开发.掌握数据库一直感觉还不错.但是今天就出现了一个问题困扰我大半天.后来本来准备睡觉,但是觉得今天不解决这个问题恐怕晚上是“彻夜难眠啊”!!于是打开电脑,又开始捣腾 ...

  2. 递归方程T(n)=aT(n/b)+f(n)之通用解法

    ,b>1为常数,f(n)为函数,T(n)=aT(n/b)+f(n)为非负数,令x=logba: 1.       f(n)=o(nx-e),e>0,那么T(n)=O(nx). 2.     ...

  3. Pwn with File结构体之利用 vtable 进行 ROP

    前言 本文以 0x00 CTF 2017 的 babyheap 为例介绍下通过修改 vtable 进行 rop 的操作 (:-_- 漏洞分析 首先查看一下程序开启的安全措施 18:07 haclh@u ...

  4. mybatis 一对多 id标签作用

    一对多 MyBatis的resultMap只用于配置结果如何映射,id的唯一作用就是在嵌套的映射配置时判断数据是否相同,当配置id标签时,MyBatis只需要逐条比较所有数据中id标签字段值是否相同即 ...

  5. Eclipse 下载、安装、取消自动更新、设置编码、关联Tomcat

    1.下载 (1)安装jdk 如果进行web开发,下载java se 版本的jdk即可,不需要像安装 java se 一样安装java ee(里面大多是接口和抽象类).关于java ee的依赖问题有两种 ...

  6. 2 python全局变量如何指定(是在模块内,还是函数内)

    示例代码1 xx=1 def __recurrence(): # #前面的0到sentence_length-1的下标,存储的就是最原始的词向量,但是我们也要将其转变为Tensor global xx ...

  7. shell_script1

    1.简介 2.read 3.运算工具 4.if/then结构 5.while循环 6.for循环   一.简介 1.什么是shell shell是用户与系统交互作用的界面.shell是一种命令解释程序 ...

  8. MATLAB filter2/conv2 函数在 Python 语言中的等价函数

    MATLAB filter2 和 conv2 函数说明 在 MATLAB 中,filter2 函数实现二维数字滤波器.conv2 函数实现二维卷积. filter2(H, X, mode) 等价于 c ...

  9. C++易混淆概念

    1. 引用和指针有什么区别? 本质:一个是别名,一个是地址1. 指针可以在运行时改变其所指向的值,引用一旦和某个对象绑定就不再改变2. 引用没有const, 指针有const 3. 从内存上看,指针会 ...

  10. java vector的多线程安全是否有用

    在网上搜了不少文章,发现有不少没讲清楚的,也有不少好文,本文希望更易懂地描述该问题.如有不对的地方,请多多指正~~ vector的使用主要有如下两种场景:(1)vector所谓的多线程安全,只是针对单 ...