Effective C++:参考自harttle land

类的swap实现与STL容器是一致的:提供swap成员函数, 并特化std::swap来调用那个成员函数

 class Widget {
public:
void swap(Widget& other){
using std::swap; // 使得`std::swap`在该作用域内可见
swap(pImpl, other.pImpl);
}
}; namespace std {
template<>
void swap<Widget>(Widget& a, Widget& b){
a.swap(b); // 调用成员函数
}
}

类模板的Swap

1.下面代码不能通过编译。C++允许偏特化类模板,却不允许偏特化函数模板

template<typename T>
class WidgetImpl { ... }; template<typename T>
class Widget { ... }; namespace std {
template<typename T>
// swap后的尖括号表示这是一个特化,而非重载。
// swap<>中的类型列表为template<>中的类型列表的一个特例。
void swap<Widget<T> >(Widget<T>& a, Widget<T>& b){
a.swap(b);
}
}

2. 不能向STL里面添加新的东西,重载版本。C++标准中,客户只能特化std中的模板,但不允许在std命名空间中添加任何新的模板,会引发未定义

namespace std {
template<typename T>
// 注意swap后面没有尖括号,这是一个新的模板函数。
// 由于当前命名空间已经有同名函数了,所以算函数重载。
void swap(Widget<T>& a, Widget<T>& b){
a.swap(b);
}
}

正确做法是不在std下添加swap函数了,把swap定义在Widget所在的命名空间中,或直接在global命名空间:

namespace WidgetStuff {
template<typename T>
class Widget {
public:
void swap(Widget& other){
using std::swap;
swap(pImpl, other.pImpl);
}
}; template<typename T>
void swap(Widget<T>& a, Widget<T>& b){
a.swap(b);
}
}

似乎类的Swap也只需要在同一命名空间下定义swap函数,而不必特化std::swap。 但是!有人喜欢直接写std::swap(w1, w2),调用std::swap,WidgetStuff::swap不会得到调用。特化std::swap可以让你的类更加健壮。此时,C++编译器会优先调用指定了T的std::swap特化版,其次是T所在命名空间下的对应swap函数, 最后才会匹配std::swap的默认实现。

应用:

赋值构造函数的swap写法:解决自己赋值自己的问题

class Widget
{
public:
Widget():id(), size(), data(NULL){}
Widget(int val)
{
id = val;
size = id+;
data = new int [size];
}
Widget(const Widget &rhs) //deep copy
{
id = rhs.id;
size = rhs.size;
data = new int[size];
memcpy(data, rhs.data, sizeof(int)*size);
}
friend void swap(Widget & lhs, Widget &rhs)
{
using std::swap;
swap(lhs.id, rhs.id);
swap(lhs.size, rhs.size);
swap(lhs.data, rhs.data);
}
  /*
Widget & operator = (Widget rhs) //pass by value, copy构造后swap
{
swap(*this, rhs);
return *this;
}
*/
Widget& operator= (const Widget &rhs) //pass by 引用
{
Widget tmp(rhs);
swap(*this, tmp);
return *this;
}
~Widget()
{
std::cout<<"id:"<< id << " destructor called..." << std::endl;
if(data != NULL)
{
delete [] data;
data = NULL;
}
}
int getId() const
{
return id;
}
int getSize() const
{
return size;
} private:
int *data;
int id;
int size;
};

如何实现Swap呢?总结一下:

  1. 提供一个更加高效的,不抛异常的共有成员函数(比如Widget::swap)。
  2. 在你类(或类模板)的同一命名空间下提供非成员函数swap,调用你的成员函数。
  3. 如果你写的是类而不是类模板,请偏特化std::swap,同样应当调用你的成员函数。
  4. 调用时,请首先用using使std::swap可见,然后直接调用swap

考虑实现一个不抛异常的swap的更多相关文章

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

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

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

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

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

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

  4. [Effective C++ --025]考虑写出一个不抛异常的swap函数

    引言 在我的上一篇博客中,讲述了swap函数. 原本swap只是STL的一部分,而后成为异常安全性编程的脊柱,以及用来处理自我赋值可能性. 一.swap函数 标准库的swap函数如下: namespa ...

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

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

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

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

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

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

  8. 【JAVA】学习路径64-补充-编写一个会抛异常的方法

    有一些方法,在调用的时候有可能会出错,所以我们使用这些方法的时候会使用try catch. 比如InputStream里面的read()方法等等,那么这些方法是怎么实现抛异常的效果的呢? 能抛异常的方 ...

  9. 不抛异常的swap函数

    namespace AStuff{ template<typename T> class A { public: void swap(A *other) { using std::swap ...

随机推荐

  1. Django 的系统时区设置 RPC

    PRC   Deprecated +08:00 +08:00 Link to Asia/Shanghai settings.py  文件的设置项:   TIME_ZONE = 'PRC'     来源 ...

  2. mongodb管理与安全认证

    mongodb数据管理 数据的导出.数据导入数据导出 mongoexport [使用mongoexport -h查看参数] 数据导入 mongoimport [使用mongoimport -h查看参数 ...

  3. 题解-AtCoder Code-Festival2017qualA-E Modern Painting

    Problem CODE-FESTIVAL 2017 qual A 洛谷账户的提交通道 题意:有一个\(n\)行\(m\)列的方格,在边界外有可能有机器人(坐标为\((0,x),(n+1,x),(x, ...

  4. OTP

    OTP 是 One Time Programable, 一次性可编程,一种存储器类型.顾名思义,只允许一次编程,后面无法修改. 在嵌入式系统当中,所有的代码和系统数据都是存储在flash芯片内部的,f ...

  5. JS将图片转换成Base64码

    直接上代码 html页面代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  6. Golang的单引号、双引号与反引号

    Go语言的字符串类型string在本质上就与其他语言的字符串类型不同: Java的String.C++的std::string以及Python3的str类型都只是定宽字符序列 Go语言的字符串是一个用 ...

  7. 洛谷P4336 [SHOI2016]黑暗前的幻想乡 [Matrix-Tree定理,容斥]

    传送门 思路 首先看到生成树计数,想到Matrix-Tree定理. 然而,这题显然是不能Matrix-Tree定理硬上的,因为还有每个公司只能建一条路的限制.这个限制比较恶心,尝试去除它. 怎么除掉它 ...

  8. 大数据mapreduce俩表join之python实现

    二次排序 在Hadoop中,默认情况下是按照key进行排序,如果要按照value进行排序怎么办?即:对于同一个key,reduce函数接收到的value list是按照value排序的.这种应用需求在 ...

  9. IOS 常遇到的报错警告 以及 解决办法

    1.  This application is modifying the autolayout engine from a background thread, which can lead to ...

  10. Oracle 高水位说明和释放表空间,加快表的查询速度

    高水位的介绍 数据库运行了一段时间,经过一些列的删除.插入.更改操作有些表的高水位线就有可能和实际的表存储数据的情况相差特别多,为了提高检索该表的效率,建议对这些表进行收缩: 查找高水位线的表 查找表 ...