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. jQuery插件开发jQuery.extend(object)和jQuery.fn.extend()

    jQuery插件开发 知识1:用JQuery写插件时,最核心的方法有如下两个: $.extend(object) 可以理解为JQuery 添加一个静态方法. $.fn.extend(object) 可 ...

  2. jquery源码解析

    //局部作用域,外部引用不到这个闭合函数里面的东西,这时候需要用提供的对外访问接口来访问里面的变量 (function(){ ; function $() { alert(a) } window.$ ...

  3. 数据库的OLE字段写入长二进制文件

    //'*************************************************************************************** //'函数:将数据 ...

  4. 【转】JVM内存结构 VS Java内存模型 VS Java对象模型

    JVM内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途. 其中有些区域随着虚拟机进程的启动而 ...

  5. NOI2019 SX 模拟赛 no.5

    Mas 的童年 题目描述:不知道传送门有没有用? 反正就是对于每个前缀序列求一个断点,使得断点左右两个区间的 分别的异或和 的和最大 分析 jzoj 原题? 但是我 TM 代码没存账号也过期了啊! 然 ...

  6. Unity3D ParticleSystem粒子系统

    粒子系统检视面板 点击粒子系统检视面板的右上角的"+"来增加新的模块.(Show All Modules:显示全部) 初始化模块: 持续时间(Duration):粒子系统发射粒子的 ...

  7. 认证 (authentication) 和授权 (authorization) 的区别

    authorization 授权 authentication 身份认证 用户认证流程: 1.用户使用username和password登录 2.系统验证这个password对于该username是正 ...

  8. git下载指定的版本

    1.查看提交历史    sudo git log 打印如下内容: commit 2e3c19d412ab6a99bb51f338f71537a720a9c706   Author: huangbaog ...

  9. why should the parameter in copy construction be a reference

    if not, it will lead to an endless loop!!! # include<iostream> using namespace std; class A { ...

  10. Bootstrap -- 模态框实现拖拽移动

    ### 这里实现这个效果 需要引入 jquery-ui.min.js类库 jquery-ui.min.css样式 使用它提供的draggable()方法实现 ### 菜鸟教程 http://www.r ...