设为首页 加入收藏

TOP

读书笔记 effective c++ Item 25 实现一个不抛出异常的swap(一)
2017-10-13 10:14:58 】 浏览:3335
Tags:读书 笔记 effective Item 实现 一个 异常 swap

1. swap如此重要

Swap是一个非常有趣的函数,最初作为STL的一部分来介绍,它已然变成了异常安全编程的中流砥柱(Item 29),也是在拷贝中应对自我赋值的一种普通机制Item 11)。Swap非常有用,恰当的实现swap是非常重要的,与重要性伴随而来的是一些并发症。在这个条款中,我们将探索这些并发症以及如何处理它们。

2. swap的傻瓜实现方式及缺陷

2.1 swap函数的默认实现

Swap函数就是将两个对象的值进行交换,可以通过使用标准的swap算法来实现:

 1 namespace std {
 2 
 3 template<typename T> // typical implementation of std::swap;
 4 
 5 void swap(T& a, T& b) // swaps a’s and b’s values
 6 
 7 {
 8 
 9 T temp(a);
10 
11 a = b;
12 
13 b = temp;
14 
15 }
16 
17 }

 

只要你的类型支持拷贝(拷贝构造函数和拷贝赋值运算符),默认的swap实现不需要你做一些特别的工作来支持它。

2.2 swap函数默认实现的缺陷——有可能效率低

然而,默认的swap实现也许并没有让你激动,它包括三次拷贝:a 拷贝到temp,b拷贝到a, temp拷贝到b。对于一些类型来说,这些拷贝不是必须的,默认的swap将你从快车道拉到了慢车道。

这些不需要拷贝的类型内部通常包含了指针,指针指向包含真实数据的其他类型。使用这种设计方法的一个普通的例子就是“pimpl idiom”(指向实现的指针 Item 31).举个例子:

 1 class WidgetImpl { // class for Widget data;
 2 
 3 public: // details are unimportant
 4 
 5 ...
 6 
 7 private:
 8 
 9 int a, b, c; // possibly lots of data —
10 
11 std::vector<double> v; // expensive to copy!
12 
13 ...
14 
15 };
16 
17 class Widget { // class using the pimpl idiom
18 
19 public:
20 
21 Widget(const Widget& rhs);
22 
23 Widget& operator=(const Widget& rhs) // to copy a Widget, copy its
24 
25 { // WidgetImpl object. For
26 
27 ... // details on implementing
28 
29 *pImpl = *(rhs.pImpl); // operator= in general,
30 
31 ... // see Items 10, Item 11, and Item 12.
32 
33 }
34 
35 ...
36 
37 private:
38 
39 WidgetImpl *pImpl; // ptr to object with this
40 
41 }; // Widget’s data

 

 

为了交换两个Widget对象的值,我们实际上唯一需要做的是交换两个pImpl指针,但是默认的swap算法没有办法能够获知这些。它不仅拷贝了三个Widget对象,还拷贝了三个WidgetImpl对象。非常没有效率,也不令人鸡冻。

3. 如何实现一个高效的swap

3.1 为普通类定义全特化版本swap

我们需要做的就是告诉std::swap当Widget对象被swap的时候,执行swap的方式是swap内部的pImpl指针。也就是为Widget定制一个std::swap。这是最基本的想法,看下面的代码,但是不能通过编译。。

 1 namespace std {
 2 
 3 template<> // this is a specialized version
 4 
 5 void swap<Widget>(Widget& a, // of std::swap for when T is
 6 
 7 Widget& b) // Widget
 8 
 9 {
10 
11 swap(a.pImpl, b.pImpl); // to swap Widgets, swap their
12 
13 } // pImpl pointers; this won’t
14 
15 compile
16 
17 }

 

开始的”templpate<>”说明这是对std::swap的模板全特化(total template specializaiton),名字后面的”<Widget>”是说明这个特化只针对T为Widget类型。换句话说,当泛化的swap模板被应用到Widget类型时,应该使用上面的实现方法。一般来说,我们不允许修改std命名空间的内容,但是却允许使用我们自己创建的类型对标准模板进行全特化

但是这个函数不能编译通过。这是因为它尝试访问a和b中的pImpl指针,它们是private的。我们可以将我们的特化函数声明成friend,但是传统做法却是这样:在Widget中声明一个真正执行swap的public成员函数swap,让std::swap调用成员函数:

 1 class Widget { // same as above, except for the
 2 
 3 public: // addition of the swap mem func
 4 
 5 ...
 6 
 7 void swap(Widget& other)
 8 
 9 {
10 
11 using std::swap; // the need for this declaration
12 
13 // is explained later in this Item
14 
15 swap(pImpl, other.pImpl); // to swap Widgets, swap their
16 
17 } // pImpl pointers
18 
19 ...
20 
21 };
22 
23 namespace std {
24 
25 template<> // revised specialization of
26 
27 void swap<Widget>(Widget& a, // std::swap
28 
29 Widget& b)
30 
31 {
32 
33 a.swap(b); // to swap Widgets, call their
34 
35 } // swap member function
36 
37 }

 

这种做法不仅编译能通过,同STL容器一致,它们都同时为swap提供了public成员函数版本和调用成员函数的std::swap版本。

3.2 为模板类定义偏特化版本swap

然而假设Widget和WidgetImpl换成了类模版,我们就将存储在WidgetImpl中的数据类型替换成一个模板参数:

1 template<typename T>
2 
3 class WidgetI
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇gcc下c++的对象模型 (1) 下一篇13:图像模糊处理

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目