We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
很多时候我们会遇到两个对象值的交换,例如std::sort使用的排序算法就是基于变量交换的,std::reverse也用到了交换。交换效率的高低决定了这些算法的执行时间。
std::sort
std::reverse
然而,交换操作往往会带来不必要的复制。在<utility>中定义了std::swap可以实现变量的交换,我们先看看std::swap的实现。
<utility>
std::swap
template<typename T> void swap(T &a,T &b) noexcept { T temp = std::move(a); a = std::move(b); b = std::move(temp); }
参见L6-引用与复制PPT,这样实现swap,可以避免3次不必要的拷贝操作,只是有一个前提要求——类型T必须同时重载了移动构造函数和移动赋值运算符。如果类型T只重载了拷贝构造函数和拷贝赋值运算符,那么使用swap,多余的拷贝操作仍无法避免。
swap
在类型T没有实现移动操作的情况下,我们可以自己实现swap函数。
以第三次作业http://115.182.62.169:8000/contest/106/problem/80为例,交换两个Vector,只需要交换array指针和成员变量len、capacity即可,实现起来非常容易:
void swap(Vector &a,Vector &b) { using std::swap; // 用std::swap交换变量的值 swap(a.array,b.array); swap(a.len,b.len); swap(a.capacity,b.capacity); }
你可能会问,实现了这个swap函数之后,真的就能够加快std::reverse等算法了吗?答案是肯定的。我们不妨来看看std::reverse的源码是怎样的: (一种可能的实现)
template<class BidirIt> void reverse(BidirIt first, BidirIt last) { while ((first != last) && (first != --last)) { std::iter_swap(first++, last); } }
可以看到,当需要交换两个元素时,std::reverse用到了std::iter_swap。我们再来看看std::iter_swap的实现:
std::iter_swap
template<class ForwardIt1, class ForwardIt2> void iter_swap(ForwardIt1 a, ForwardIt2 b) { using std::swap; swap(*a, *b); }
为什么std::iter_swap的实现没有直接std::swap(*a, *b);,而是先using std::swap;再执行swap呢?
std::swap(*a, *b);
using std::swap;
其实,直接使用std::swap(*a, *b);并不会带来任何性能上的优势——我们自定义的swap函数并不在命名空间std内。这样虽然没有编译错误,但是程序根本没有用到我们自己写的swap函数,最后使用的仍然是没有优化过的std::swap。
std
先using std::swap;再执行swap就能很好地解决这个问题。如果我们自己写了一个swap,会将std::swap覆盖掉(这是因为函数匹配的原则:选择最为特例化的版本,因而普通函数会比函数模板std::swap优先级高)。std::swap只是在没有专门实现时才会被调用。
这也提示了我们一个关于变量交换的best practice:交换两个变量a,b时,最好用using std::swap; swap(a,b);,而不是直接std::swap(a,b);
using std::swap; swap(a,b);
std::swap(a,b);
编写自己的swap函数还有一个额外的好处。如果已经写好了类的拷贝构造函数、移动构造函数和swap函数,我们可以轻易完成赋值运算符的重载:
Vector& Vector::operator=(Vector rhs) { swap(*this,rhs); return *this; }
注意第一行rhs是按值传递的,调用了Vector的拷贝/移动构造函数,将等号右边的Vector拷贝/移动到临时变量rhs中。接着,将等号左侧的Vector和rhs交换,这样就相当于把等号右侧的Vector赋值到左侧,而交换后rhs拥有等号左侧Vector的资源。最后,函数返回时,rhs的生命周期结束,对rhs进行析构,就释放了原来等号左侧对象的资源。
而且,这样的代码可以正确处理自赋值的情况。此时rhs会拷贝一份等号右侧对象,再与等号左侧的对象交换,并不会出现任何问题。
参考: https://blog.csdn.net/wbvalid/article/details/117852626
The text was updated successfully, but these errors were encountered:
No branches or pull requests
很多时候我们会遇到两个对象值的交换,例如
std::sort
使用的排序算法就是基于变量交换的,std::reverse
也用到了交换。交换效率的高低决定了这些算法的执行时间。然而,交换操作往往会带来不必要的复制。在
<utility>
中定义了std::swap
可以实现变量的交换,我们先看看std::swap
的实现。std::swap
的一种实现参见L6-引用与复制PPT,这样实现swap,可以避免3次不必要的拷贝操作,只是有一个前提要求——类型T必须同时重载了移动构造函数和移动赋值运算符。如果类型T只重载了拷贝构造函数和拷贝赋值运算符,那么使用swap,多余的拷贝操作仍无法避免。
实现自己的类的
swap
函数在类型T没有实现移动操作的情况下,我们可以自己实现swap函数。
以第三次作业http://115.182.62.169:8000/contest/106/problem/80为例,交换两个Vector,只需要交换array指针和成员变量len、capacity即可,实现起来非常容易:
你可能会问,实现了这个swap函数之后,真的就能够加快
std::reverse
等算法了吗?答案是肯定的。我们不妨来看看std::reverse
的源码是怎样的:(一种可能的实现)
可以看到,当需要交换两个元素时,
std::reverse
用到了std::iter_swap
。我们再来看看std::iter_swap
的实现:为什么
std::iter_swap
的实现没有直接std::swap(*a, *b);
,而是先using std::swap;
再执行swap
呢?其实,直接使用
std::swap(*a, *b);
并不会带来任何性能上的优势——我们自定义的swap
函数并不在命名空间std
内。这样虽然没有编译错误,但是程序根本没有用到我们自己写的swap
函数,最后使用的仍然是没有优化过的std::swap
。先
using std::swap;
再执行swap
就能很好地解决这个问题。如果我们自己写了一个swap
,会将std::swap
覆盖掉(这是因为函数匹配的原则:选择最为特例化的版本,因而普通函数会比函数模板std::swap
优先级高)。std::swap
只是在没有专门实现时才会被调用。这也提示了我们一个关于变量交换的best practice:交换两个变量a,b时,最好用
using std::swap; swap(a,b);
,而不是直接std::swap(a,b);
"copy-and-swap"技巧
编写自己的swap函数还有一个额外的好处。如果已经写好了类的拷贝构造函数、移动构造函数和swap函数,我们可以轻易完成赋值运算符的重载:
注意第一行rhs是按值传递的,调用了Vector的拷贝/移动构造函数,将等号右边的Vector拷贝/移动到临时变量rhs中。接着,将等号左侧的Vector和rhs交换,这样就相当于把等号右侧的Vector赋值到左侧,而交换后rhs拥有等号左侧Vector的资源。最后,函数返回时,rhs的生命周期结束,对rhs进行析构,就释放了原来等号左侧对象的资源。
而且,这样的代码可以正确处理自赋值的情况。此时rhs会拷贝一份等号右侧对象,再与等号左侧的对象交换,并不会出现任何问题。
参考:
https://blog.csdn.net/wbvalid/article/details/117852626
The text was updated successfully, but these errors were encountered: