(一)

缺省情况下swap动作可由标准程序库提供的swap算法完毕:

namespace std {
template<typename T>
void swap(T& a, T& b) {
T temp(a);
a = b;
b = temp;
}
}

这个函数是异常安全性编程的核心,而且是用来处理自我赋值可能性的一个常见机制

可是对某些类型而言,这些复制动作无一必要:当中基本的就是“以指针指向一个对象,内含真正数据”那种类型。多为“pimpl手法”(pointer
to implementation的缩写)

<span style="color:#333333;"><span style="font-family:Verdana, Arial, Helvetica, sans-serif;font-size:12px;">class WidgetImpl {
private:
int a, b, c;
std::vector<double> v;
};
class Widget {
public:
Widget(const Widget& rhs);
Widget& operator=(const Widget& rhs) {
*pImpl = *(rhs.pImpl);
}
private:
WidgetImpl* pImpl;
};</span></span>

要置换两个Widget对象值。唯一要做的就是置换pImpl指针,缺省的swap算法不知道这一点。不仅仅复制3个Widget还复制3个WidgetImpl对象。很缺乏效率!

解决的方法:

我们能够令Widget声明一个swap的public成员函数做真正的替换工作,然后将std::swap特化。令他调用该成员函数:

<span style="color:#333333;"><span style="font-family:Verdana, Arial, Helvetica, sans-serif;">class Widget {
public:
Widget(const Widget& rhs);
Widget& operator=(const Widget& rhs) {
*pImpl = *(rhs.pImpl);
}
void swap(Widget& other) {
using std::swap;
swap(pImpl, other.pImpl);
}
private:
WidgetImpl* pImpl;
}; </span></span><pre name="code" class="cpp" style="line-height: 19px; text-align: justify;">class WidgetImpl {
private:
int a, b, c;
std::vector<double> v;
};

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


这样的做法不仅仅可以通过编译,还与STL容器有一致性。由于全部STL容器也都提供有public
swap成员函数和std::swap特化版本号(用以调用前者)。

(二)

如果Widget和WidgetImpl都是class templates而非classes:

<span style="color:#333333;"><span style="font-family:Verdana, Arial, Helvetica, sans-serif;">template <typename T>
class WidgetImpl{…};
template <typename T>
class Widget{…};</span></span>

所谓的“partially specialize“:C++同意对类模板进行”部分特化“。但不同意对模板函数进行”部分特化“。

以下的定义就是错误的(将上例中的Widget类写成模板类):

namespace std{
template<typename T>
void swap<Widget<T>>(Widget<T>& a, Widget<T>& b){ //错误。不合法! a.swap(b);
}
}

即使加入重载版本号也行不通。由于标准命名空间是一个特殊的命名空间,客户能够全特化里面的模板,可是不能加入新的模板(包含类模板和函数模板)到标准命名空间中去。所以以下这样的方法不行!

namespace std{
template<typename T>
void swap(Widget<T>& a, Widget<T>& b){ //试图重载,不合法。
a.swap(b);
}
}

一般而言,重载function template没有问题,但std是个特殊的命名空间。管理也就比較特殊。客户能够全特化std内的templates。但不能够加入新的templates(或class或function或不论什么其它东西)到std里头。事实上跨越红线的程序差点儿仍可编译运行,但他们行为没有明白定义。所以不要加入不论什么新东西到std里头。

解决方法:

为了提供较高效的template特定版本号。我们还是声明一个non-member
swap但不再是std::swap的特化版本号或重载版本号:

namespace WidgetStuff {
template<typename T>
class Widget{...};
template<typename T>
void swap(Widget<T>& a, Widget<T>& b) {
a.swap(b);
}
}

如今。不论什么地点的不论什么代码假设打算置换两个Widget对象,因而调用swap,C++的名称查找法则就是所谓“argument-dependent lookup”会找到WidgetStuff内的专属版本号。

(三)

template<typename T>
void doSomething(T& obj1, T& obj2) {
swap(obj1, obj2);
}

上面的应该使用哪个swap?是std既有的那个一般化版本号还是某个可能存在的特化版本号等?你希望应该是调用T专属版本号。并在该版本号不存在的情况下调用std内的一般化版本号。以下是你希望发生的事:

template<typename T>
void doSomething(T& obj1, T& obj2) {
using std::swap; //令std::swap在此函数内可用
swap(obj1, obj2);
}

c++的名称查找法则(name lookup rules)确保将找到global作用域或T所在之命名空间内的不论什么T专属的swap。假设T是Widget而且位于命名空间WidgetStuff内,编译器会找出WidgetStuff内的swap。

假设没有T专属的swap存在。编译器就是用std内的swap,然而即便如此。编译器还是比較喜欢std::swap的T专属特化版本号,而非一般化的那个template。

总结:

(1)假设swap的缺省实现码对你的class或class template提供可接受的效率。那么我们不须要额外做不论什么事。

(2)假设swap缺省实现版效率不足(某种pimpl),那么我们就像这样做:

1.提供一个public swap成员函数。这个函数绝不该抛出异常。

2.在class或template所在的命名空间内提供一个non-member swap, 并令他调用上述swap成员函数。

3.假设正编写一个class(而非class template),为你的class特化std::swap。并令他调用swap成员函数。

假设调用swap,确保包括一个using声明式,以便让std::swap在你的函数内曝光可见,然后不加不论什么namespace修饰符,赤裸裸调用swap。

swap的一个最好的应用是帮助classes(class templates)提供强烈的异常安全性保障。(条款29对此提供了全部细节)此技术基于一个如果:成员版的swap绝不抛出异常。当你写一个自定版本号的swap,提供的不仅仅是高效置换对象值的办法,并且不抛出异常。一般,这两个swap特性是连在一起的,由于高效的swaps差点儿总是基于对内置类型的操作(比如pimpl手法的底层指针),而内置类型上的操作绝不会抛出异常。

请记住:

(1)当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。

(2)假设你提供一个member swap,也该提供一个non-member swap用来调用前者.对于classes(而非template),也请特化std::swap。

(3)调用swap时应针对std::swap使用using声明式,然后调用swap而且不带不论什么"命名空间资格修饰“。

(4)为"用户定义类型"进行std templates全特化是好的,但千万不要尝试在std内增加某些std而言全新的东西。

Effective C++:条款25:考虑写出一个不抛异常的swap函数的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

  7. 考虑实现一个不抛异常的swap

    Effective C++:参考自harttle land 类的swap实现与STL容器是一致的:提供swap成员函数, 并特化std::swap来调用那个成员函数. class Widget { p ...

  8. effective C++ 条款25 swap

    item 25:一个不抛异常的swap函数 标准库有一个swap用于交换两个对象值 namespace std{ template<typename T> void swap(T& ...

  9. 输入一个数字n 如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数 写出一个函数

    题目: 输入一个数字n  如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数  写出一个函数 首先,这道题肯定可以用动态规划来解, n为整数时,n的解为 n/2 的解加1 n为奇数时 ...

随机推荐

  1. linux kernel的函数与抽象层

    在数学领域,函数是一种关系,这种关系使一个集合里的每一个元素对应到另一个(可能相同的)集合里的唯一元素. 在C语言中函数也有这种联系.自变量影响着因变量. 在linux内核驱动编程经常会有抽象层的概念 ...

  2. java学习笔记-Thread

    java定义了创建线程的两种方法 1.实现Runnable接口 2.扩展Thread类本身 1.实现Runnable接口: 可以依托任何Runnable接口的对象来创建线程.但是为了实现Runnabl ...

  3. LeetCode——Populating Next Right Pointers in Each Node II

    Follow up for problem "Populating Next Right Pointers in Each Node". What if the given tre ...

  4. 中国大概可用NTPserver地址

    133.100.11.8 prefer210.72.145.44203.117.180.36131.107.1.10time.asia.apple.com64.236.96.53130.149.17. ...

  5. codeforces 598D Igor In the Museum

    题目链接:http://codeforces.com/problemset/problem/598/D 题目分类:dfs 题目分析:处理的时候一次处理一片而不是一个,不然会超时 代码: #includ ...

  6. Apache+windows server2008 外网访问配置

    之前在一个服务器上部署一个apache网站,在局域网内都可以访问,但是外网始终访问不了,经常多次谷歌,把解决方案总结出来. 下面就默认部署apache自带的网站.系统:windows server20 ...

  7. ps中图层混合模式、多图层叠加、不透明度、填充、图层样式详解

    图像领域中,通过进行一下想法的时候,都要通过用ps看下是不是合理,而ps中图层是必用的一个功能,下面详解一下图层有关的叠加原理. 基本顺序是图层从下往上继续, 先计算图层的填充,再计算样式.最后计算不 ...

  8. Apache Commons IO入门教程(转)

    Apache Commons IO是Apache基金会创建并维护的Java函数库.它提供了许多类使得开发者的常见任务变得简单,同时减少重复(boiler-plate)代码,这些代码可能遍布于每个独立的 ...

  9. JavaFX2: 鼠标拖动选择和Ctrl+Shift连续区间选择的ListView

    JavaFX2的ListView中的多选没有提供鼠标拖动选择的功能,同时按下Ctrl和Shift后连续的区间选中也不支持,以下代码用于处理这两个问题,细节见代码注释: import com.sun.j ...

  10. Android仿iOS7的UISegmentedControl 分段

    效果图: 这里仅仅简单做了两个button的. 首先是两个button的背景: res/drawable/seg_left.xml <?xml version="1.0" e ...