在这个阶段,编写复制构造函数和赋值运算符是定义良好的;快速搜索会导向许多如何正确编写这些函数的结果。现在移动构造函数已经加入了其中,是否有一种新的“最佳”方法呢?
最好的情况是,它们只需是= default;
,因为成员类型应该是资源管理类型,可以从您隐藏移动细节,比如std::unique_ptr
。只有那些“低级”类型的实现者才需要处理这个问题。
请记住,仅当您持有外部(对于您的对象而言)资源时才需要关注移动语义。对于“平面”类型来说,它是完全无用的。
最好的方法是让编译器生成它们。这也是C++03中最好的方法,如果您成功实现了这一点,当您迁移到C++11时,您的C++03类将自动成为“可移动启用”。
大多数资源管理问题可以通过编写单一资源管理类的非复制构造函数和析构函数来解决,然后只使用这些类来创建组合类,再加上智能指针(例如std::unique_ptr
)和容器类来构建更丰富的对象。
#include <chrono>
#include <iostream>
#include <vector>
#if SLOW_DOWN
class MyClass
{
void Swap(MyClass &other)
{
std::swap(other.member, member);
}
public:
MyClass()
: member()
{
}
MyClass(const MyClass &other)
: member(other.member)
{
}
MyClass(MyClass &&other)
: member(std::move(other.member))
{
}
MyClass &operator=(MyClass other)
{
other.Swap(*this);
return *this;
}
private:
int member;
};
#else
class MyClass
{
public:
MyClass()
: member()
{
}
private:
int member;
};
#endif
int main()
{
typedef std::chrono::high_resolution_clock Clock;
typedef std::chrono::duration<float, std::milli> ms;
auto t0 = Clock::now();
for (int k = 0; k < 100; ++k)
{
std::vector<MyClass> v;
for (int i = 0; i < 1000000; ++i)
v.push_back(MyClass());
}
auto t1 = Clock::now();
std::cout << ms(t1-t0).count() << " ms\n";
}
$ clang++ -stdlib=libc++ -std=c++11 -O3 -DSLOW_DOWN test.cpp
$ a.out
519.736 ms
$ a.out
517.036 ms
$ a.out
524.443 ms
$ clang++ -stdlib=libc++ -std=c++11 -O3 test.cpp
$ a.out
463.968 ms
$ a.out
458.702 ms
$ a.out
464.441 ms
memcpy
来复制您的类,甚至可以复制大型数组。因此,最好让您的特殊成员变得简单(trivial),如果可以的话。这意味着让编译器定义它们。尽管您仍然可以使用= default
声明它们,如果您愿意的话。class MyClass
{
void Swap(MyClass &other)
{
std::swap(other.member, member);
}
public:
MyClass()
: member()
{
}
MyClass(const MyClass &other)
: member(other.member)
{
}
MyClass(MyClass &&other)
: member(std::move(other.member))
{
}
MyClass &operator=(MyClass other)
{
other.Swap(*this);
return *this;
}
private:
int member;
};
MyClass
确实应该具有这些语义,那么这可能是编写特殊成员的最糟糕(性能最差)的方式,尽管仍然被认为是正确的。抱歉这么直接,但我认为你应该知道。 - Howard HinnantMyClass lC = MyClass(), lC1; lC1 = lC;
这样的操作时会发生什么? - lapk