我一直在测试一些 C++11 功能。
我遇到了右值引用和移动构造函数。
我实现了我的第一个移动构造函数,如下:
#include <iostream>
#include <vector>
using namespace std;
class TestClass{
public:
TestClass(int s):
size(s), arr(new int[s]){
}
~TestClass(){
if (arr)
delete arr;
}
// copy constructor
TestClass(const TestClass& other):
size(other.size), arr(new int[other.size]){
std::copy(other.arr, other.arr + other.size, arr);
}
// move constructor
TestClass(TestClass&& other){
arr=other.arr;
size=other.size;
other.arr=nullptr;
other.size=0;
}
private:
int size;
int * arr;
};
int main(){
vector<TestClass> vec;
clock_t start=clock();
for(int i=0;i<500000;i++){
vec.push_back(TestClass(1000));
}
clock_t stop=clock();
cout<<stop-start<<endl;
return 0;
}
该代码运行良好。无论如何,将 std::cout 放入复制构造函数中,我注意到它被调用了!而且很多次..(移动构造函数 500000 次,复制构造函数 524287 次)。
更让我惊讶的是,如果我从代码中注释掉复制构造函数,整个程序就会快很多,而且这次移动构造函数被调用了1024287次。
有什么线索吗?
Put noexcept
在你的移动构造函数中:
TestClass(TestClass&& other) noexcept {
详细说明:我本来打算给这个 Pierre,但不幸的是 cppreference 源只是大致正确。
In C++03
vector<T>::push_back(T)
拥有“强有力的例外保障”。这意味着如果push_back
抛出异常,向量保持与调用之前相同的状态push_back
.
如果移动构造函数抛出异常,这种保证就会出现问题。
当。。。的时候vector
重新分配,它会like to move将元素从旧缓冲区转移到新缓冲区。但是,如果其中任何一个移动抛出异常(除了第一个),那么它就会处于旧缓冲区已被修改的状态,并且新缓冲区尚未包含应有的所有内容。这vector
无法将旧缓冲区恢复到原始状态,因为它必须将元素移回原来的状态,those动作也可能会失败。
因此为 C++11 制定了一条规则:
If T
has a noexcept
move 构造函数,可用于将元素从旧缓冲区移动到新缓冲区。
否则如果T
有一个复制构造函数,将被使用。
否则(如果没有可访问的复制构造函数),那么最终将使用移动构造函数,但在这种情况下,不再给出强异常安全保证。
澄清:规则 2 中的“复制构造函数”意味着构造函数采用const T&
,不是那些所谓的小人之一T&
复制构造函数。 :-)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)