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. Windows PowerShell 入門(2)-基本操作編 2

    前回に引き続きMicrosoftが提供している新しいシェル.Windows Power Shellの基本操作方法を学びます.基本操作編第2弾の今回は.パイプの使用方法を中心としたコマンドレットの操作方 ...

  2. 设计模式C++学习笔记之十一(Bridge桥梁模式)

      桥梁模式,将抽象部分与它的实现部分分离,使它们都可以独立地变化.实现分离的办法就是增加一个类, 11.1.解释 main(),客户 IProduct,产品接口 CHouse,房子 CIPod,ip ...

  3. 使用位图字体工具BMFont从图片生成自定义字体

    转载自:http://blog.csdn.net/keshuiyun/article/details/9960667 BMFont下载地址: http://www.angelcode.com/prod ...

  4. 通过URL传递中文参数的乱码处理

    环境:web.xml中配置了 <filter> <filter-name>encodingFilter</filter-name> <filter-class ...

  5. selenium——获取元素的尺寸、文本信息、元素的属性、元素是否可见

    [is_disabled 可以用来检查元素是否存在]

  6. ASP.NET提供三种主要形式的缓存

    ASP.NET提供三种主要形式的缓存:页面级输出缓存.用户控件级输出缓存(或称为片段缓存)和缓存API.

  7. nginx负载均衡后端tomcat无法加载js资源

    JS或css无法完全加载 nginx的代理缓存区,默认较小导致部分文件出现加载不全的问题,比较典型的如jQuery框架,可以通过配置调整nginx的缓存区即可.主要参考proxy参数 最终完整配置如下 ...

  8. filter(HTML)滤镜用法

    CCS滤镜参考语法:STYLE="filter:filtername(fparameter1, fparameter2...)" (Filtername为滤镜的名称,fparame ...

  9. swift 学习- 16 -- 构造过程 02

    // 类的继承 和 构造过程 // 类里面的所有的存储型属性 -- 包括所有继承自父类的属性 -- 都必须在构造过程中设置初始值 // Swift  为类类型提供了 两种构造器来确保实例中所有的存储属 ...

  10. python 列表,字典,元组,字符串,常用函数

    飞机票 一.列表方法 1.ls.extend(object) 向列表ls中插入object中的每个元素,object可以是字符串,元组和列表(字符串“abc”中包含3个元组),相当于ls和object ...