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

引言

在我的上一篇博客中,讲述了swap函数。

原本swap只是STL的一部分,而后成为异常安全性编程的脊柱,以及用来处理自我赋值可能性。

一、swap函数

标准库的swap函数如下:

1 namespace std {
2     template<typename T>
3     void swap(T &a, T& b)
4     {
5             T temp(a);
6             a = b;
7             b = temp;
8     }
9 }

只要类型T支持拷贝(通过拷贝构造函数和拷贝赋值操作符完成),那么这个版本的swap函数就会帮你置换类型为T的对象。

二、Pimpl情境下的swap函数

假设我们有如下的类。

 1 class WidgetImpl {
 2 public:
 3     ...
 4 private:
 5     int a,b,c;                        // 可能有许多数据
 6     std::vector<double> v;            // 意味复制时间很长
 7     ....
 8 }
 9
10 class Widget {
11 public:
12     Widget(const Widget& rhs);
13     Widget& operator = (const Widget& rhs)
14     {
15          .....
16          *pImpl = *(rhs.pImpl);
17      }
18 private:
19     WidgetImpl* pImpl;
20 };

一旦要置换两个Widget对象,我们唯一需要做的就是置换其pImpl指针,但是一种的swap函数不知道这一点,它会复制三个Widget和三个WidgetImpl,效率非常低下!

所以我们希望的效果是:只交换pImpl指针!

因此我们可以设计出这个思路:

1 namespace std {
2     template<>          // 表示它是std::swap的一个特化版本
3     void swap<Widget>(Widget &a, Widget& b)
4     {
5          swap(a.pImpl, b.pImpl);
6     }
7 }

上述代码当然通不过编译,因为pImpl是私有成员。或许你说我们可以用friend函数来解决这一问题,可是这不大符合预期!

那我们再包装一下:

 1 class Widget {
 2 public:
 3     ...
 4     void swap(Widget& other)
 5     {
 6         using std::swap;
 7         swap(pImpl, other.pImpl);      // 若置换Widgets就置换其pImpl指针
 8     }
 9     ....
10 }
11
12 namespace std {
13     template<>                         // 表示它是std::swap的一个特化版本
14     void swap<Widget>(Widget& a, Widget& b)
15     {
16          a.swap(b);
17     }
18 }

这样不仅通过编译,还与STL容器具有一致性,因为所有的STL容器也都提供有public swap成员函数和std::swap特化版本。

但需要注意的是:C++只允许对class templates偏特化,在function template上特化是行不通的。如下面代码是编译不过的。

1 namespace std {
2     template<typename T>
3     void swap<Widget<T>>(Widget<T>& a,
4                          Widget<T>& b)
5     {
6          swap(a.pImpl, b.pImpl);
7     }
8 }

三、function template偏特化

那么,如果我们想在function template上特化,应该怎么做呢?答案是添加一个重载版本!

如下:

1 namespace std {
2     template<typename T>
3     void swap(Widget<T>& a,            // 注意这里没有“<Widget<T>>”
4               Widget<T>& b)
5     {
6          swap(a.pImpl, b.pImpl);
7     }
8 }

同时,为了其他人调用swap时能取得我们提供的较高版本的template特定版本,我们可以声明一个non-member函数!

 1 namespace Widget {
 2     template<typename T>
 3     class Widget{....}
 4
 5     template<typename T>
 6     void swap(Widget<T>& a,            // 注意这里没有“<Widget<T>>”
 7               Widget<T>& b)
 8     {
 9          swap(a.pImpl, b.pImpl);
10     }
11 }

◆总结

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

2.如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于classes,也请特化std::swap。

3.调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。

4.为“用户定义类型”进行std template全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西

时间: 2024-12-09 14:20:54

[Effective C++ --025]考虑写出一个不抛异常的swap函数的相关文章

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

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常 示例: stl里的swap算法 namespace std{ template<typename T> void swap(T &a, T &b){ T temp(a); a = b; b = temp; } } //"pimpl手法"(pointer

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

std::swap()是个很有用的函数,它可以用来交换两个变量的值,包括用户自定义的类型,只要类型支持copying操作,尤其是在STL中使用的很多,例如: int main(int argc, _TCHAR* argv[]) { int a[10] = {1,2,3,4,5,6,7,8,9,10}; vector<int> vec1(a, a + 4); vector<int> vec2(a + 5, a + 10); swap(vec1, vec2); for (int i =

条款25:考虑写出一个不抛异常的swap函数

条款25:考虑写出一个不抛异常的swap函数 swap函数在C++中是一个非常重要的函数,但实现也非常复杂. 看一个缺省的std::swap函数的实现 namespace std { template<typename T> void swap( T& a , T& b) { T temp(a); a = b; b = temp } } ①内置类型的调用 int a = 2; int b =3; std::swap(a, b); cout<<"a:&quo

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

(一) 缺省情况下swap动作可由标准程序库提供的swap算法完毕: namespace std { template<typename T> void swap(T& a, T& b) { T temp(a); a = b; b = temp; } } 这个函数是异常安全性编程的核心,而且是用来处理自我赋值可能性的一个常见机制 可是对某些类型而言,这些复制动作无一必要:当中基本的就是"以指针指向一个对象,内含真正数据"那种类型.多为"pimpl手

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

记住: ★当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定其不抛出异常 ★若你提供一个member swap,也该提供一个non-member swap来调用前者.对于classes(而非templates),也请特化std::swap ★调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰” ★为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西 --

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

当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者.对于class(而非templates),也请特化std::swap. 调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”. 为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西.

《Effective C 》资源管理:条款25--考虑写出一个不抛出异常的swap函数

条款25考虑写出一个不抛出异常的swap函数 条款25:考虑写出一个不抛出异常的swap函数 swap是STL中的标准函数,用于交换两个对象的数值.后来swap成为异常安全编程(exception-safe programming,条款29)的脊柱,也是实现自我赋值(条款11)的一个常见机制.swap的实现如下: namespace std{ template<typename T> void swap(T& a, T& b) { T temp(a); a=b; b=temp;

写出一个缓存系统的伪代码001

/** * 写出一个缓存系统的伪代码 * @author ysloong * */ public class CacheDemo { private Map<String, Object> map = new HashMap<String, Object>(); public static void main(String[] args) { // TODO Auto-generated method stub } public synchronized Object getDat

一个文件中有40亿个整数,每个整数为四个字节,内存为1GB,写出一个算法:求出这个文件里的整数里不包含的一个整数

4个字节表示的整数,总共只有2^32约等于4G个可能.为了简单起见,可以假设都是无符号整数.分配500MB内存,每一bit代表一个整数,刚好可以表示完4个字节的整数,初始值为0.基本思想每读入一个数,就把它对应的bit位置为1,处理完40G个数后,对500M的内存遍历,找出一个bit为0的位,输出对应的整数就是未出现的.算法流程:1)分配500MB内存buf,初始化为02)unsigned int x=0x1;  for each int j in file  buf=buf|x<<j;  e