美文网首页
Effective c++ 学习笔记(item25)

Effective c++ 学习笔记(item25)

作者: 懒生活 | 来源:发表于2022-09-09 23:33 被阅读0次

这里主要介绍自定义swap的惯用法

# 什么时候需要copy-swap惯用法

当编程人员要自己实现一个类来维护资源的时候。因为指针的存在,往往必不可少的要自己实现big three 函数(分别是拷贝构造函数,拷贝赋值函数,和析构三大函数)。析构和拷贝构造的实现相对直观。但是拷贝赋值操作就比较难实现,它的实现涉及到先要把被赋值的对象数据清空,然后设置成跟目标一样的数据。问题在于清空后万一异常了怎么处理。如果是先清空后赋值的顺序,那自己给自己赋值的时候就会错误。如果清空后,但是赋值的时候异常了,也会有问题。并且它的实现往往包含了拷贝构造和析构的实现,代码存在冗余。

copy and swap惯用法,在这个场合下使用能优雅的帮助拷贝赋值函数一方面减少代码冗余,另一方面异常安全。

# copy and swap的优点

举个例子,如果你要用普通方法自己写一个拷贝赋值函数。那么函数的实现要注意如下几点

1) 在函数的起始要有是否是自我赋值的判断,如果是自我赋值需要直接返回。

2)拷贝赋值操作符=的实现定义,一定要返回引用。这是唯一一类能返回引用的函数。其他的函数都是不允许返回引用的,参考item21

3)因为是赋值,赋新值之前,往往需要清理原来的旧资源,这里一定要注意先赋值后清理的原则。如果你先del原来的资源,然后new出新的资源,然后挨个复制要赋值的对象上的数据。这种流程在new的时候可能发生异常。异常会导致当前的赋值操作没有做完,但是当前对象的原数据已经被del,可能留下了一个野指针

4) copy and swap惯用法的优势是让可能出错的资源新建环节交给了编译器。看下面的代码。

```

T& operator=(T other){

swap(*this, other);

return *this;

}

```

在进入这个函数的时候,编译器已经帮你通过入参的临时构建产生了。你只要编写合适的swap函数完成临时对象和当前对象的数据替换, 对于原始资源,只需要交换指针头既可以。然后临时对象就指向旧的资源,他的析构也交给了编译器自己处理。

# copy and swap的编写注意事项

利用入参传递值,这样在函数内部会生成一个临时变量,然后吧临时变量和新变量进行交换,然后返回新变量。同事临时变量会析构。这个流程势必调用类的拷贝构造函数,和析构函数。但是优雅的是,虽然用到了,但都不是手写的代码实现,而是编译器自动调用的实现。这是优雅提现的地方。在这个流程中还用到了交换,如果这里的交换使用std::swap 那么问题就来了,std::swap的实现需要类先实现拷贝赋值函数。但是在这里我们正在编写拷贝赋值函数。所以在这种情况下,我们要自己定义swap函数,并且定义的swap函数要non-throwing.

## 自定义swap的实现

swap就是交换两个对象的数据。在std中有swap算法。std的swap算法依赖于对象的拷贝构造函数和拷贝赋值函数。他的实现类似下面的代码

```

void swap(T&a, T&b){

T temp(a);

a = b;

b = temp;

}

```

这样的实现中规中矩。实现中包括三次拷贝。对于一些特殊的类,这样的拷贝可能会引发效率低下的问题,这时候,也需要自己设计swap函数。

## 借用stl的实现方式实现我们自己的swap

```

class Widget{

public:

...

void swap(Widget& other){

    using std::swap;

    swap(pImpl, other.pImpl);

};

namespace std{

template<>

void swap<Widget>(Widget&a, Widget&b){

    a.swap(b);

```

上面的逻辑是比较绕的,但stl中大量用到了这种惯用法。当我们调用`std::swap(a,b);`编译器如果发现a是Widget类型, 那么就会调用上面的`void swap<Widget>(Widget&a, Widget&b)` 从而调用我们自定义的Widget内部的swap。 但是内部的swap有一次调用了通用的std::swap。

相关文章

网友评论

      本文标题:Effective c++ 学习笔记(item25)

      本文链接:https://www.haomeiwen.com/subject/pgwinrtx.html