(一)

缺省情况下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. HDU 1535 Invitation Cards(SPFA,及其优化)

    题意: 有编号1-P的站点, 有Q条公交车路线,公交车路线只从一个起点站直接到达终点站,是单向的,每条路线有它自己的车费. 有P个人早上从1出发,他们要到达每一个公交站点, 然后到了晚上再返回点1. ...

  2. 更好的自动ssh登录

    更好的自动ssh登录 解决~/.ssh/known_hosts 过期问题. bash + expect bash:ssh.sh #!/bin/bash help(){ echo "usage ...

  3. Windows平台Go调用DLL的坑(居然有这么多没听过的名词)

    最近的项目中,使用了GO来开发一些服务中转程序.业务比较简单,但是有一些业务需要复用原有C++开发的代码.而在WINDOWS,用CGO方式来集成C/C++代码并不是太方便.所以用DLL把C++的代码封 ...

  4. oracle 密码文件文件

    密码文件作用: 密码文件用于dba用户的登录认证. dba用户:具备sysdba和sysoper权限的用户,即oracle的sys和system用户. 本地登录: 1)操作系统认证: [oracle@ ...

  5. Linux段管理,BSS段,data段,.rodata段,text段

    近期在解决一个编译问题时,一直在考虑一个问题,那就是Linux下可执行程序执行时内存是什么状态,是依照什么方式分配内存并执行的.查看了一下资料.就此总结一下,众所周知.linux下内存管理是通过虚存管 ...

  6. 开发测试时给 Kafka 发消息的 UI 发送器――Mikasa

    开发测试时给 Kafka 发消息的 UI 发送器――Mikasa 说来话长,自从入了花瓣,整个人就掉进连环坑了. 后端元数据采集是用 Storm 来走拓扑流程的,又因为 @Zola 不是很喜欢 Jav ...

  7. Android清理设备内存具体完整演示样例(一)

    MainActivity例如以下: package come.on; import android.app.Activity; import android.content.Context; impo ...

  8. [置顶] C++学习书单

    关于C++的书太多了,很容易让人没有头绪.一直想整理下,把这些书列个书单出来,直到今天才动手.希望我的经验能给后来的初学者一点帮助. 1.<C++程序设计教程> 钱能 很多学校把这本书选做 ...

  9. Linux Shell脚本入门--grep命令详解

    grep简介<摘自鸟哥,并加以整理.> grep (global search regular expression(RE) and print out the line,全面搜索正则表达 ...

  10. Android开发系列(二十二):AdapterViewFlipper的功能和使用方法

    AdapterViewFlipper继承了AdapterViewAnimator,它会显示一个View组件,能够通过showPrevious()和showNext()方法控制组件显示上一个.下一个组件 ...