首先声明,我已经阅读了关于移动语义的许多问题。这个问题不是关于如何使用移动语义,而是在询问它的目的 - 如果我没有错的话,我不明白为什么需要移动语义。
背景
我正在实现一个复杂的类,在这个问题中,它看起来像这样:
class B;
class A
{
private:
std::array<B, 1000> b;
public:
// ...
}
当我需要创建一个移动赋值运算符时,我意识到可以通过将b成员更改为std::array<B, 1000> *b;来显着优化过程,这样移动只需删除和指针交换即可。
这让我想到了以下问题:现在,为了加快移动速度,难道不应该所有非基本类型成员都是指针吗(下面进行了更正[1][2])(有一种情况可以说明,内存不应该是动态分配的,但在这些情况下,优化移动不是问题,因为没有办法优化)?
在这里,我有了以下认识——为什么要创建一个仅仅包含指针b的类以便于后续交换,而不是直接创建一个指向整个类的指针。如果客户端期望移动比复制要快得多,则客户端应该能够接受动态内存分配。但在这种情况下,为什么客户端不直接动态分配整个类呢?
问题是:客户端不能利用指针来实现移动语义吗?如果可以,那么移动语义的目的是什么?
std::string f()
{
std::string s("some long string");
return s;
}
int main()
{
// super-fast pointer swap!
std::string a = f();
return 0;
}
指针:
std::string *f()
{
std::string *s = new std::string("some long string");
return s;
}
int main()
{
// still super-fast pointer swap!
std::string *a = f();
delete a;
return 0;
}
这里是大家都说很好的强制类型转换:
template<typename T>
T& strong_assign(T *&t1, T *&t2)
{
delete t1;
// super-fast pointer swap!
t1 = t2;
t2 = nullptr;
return *t1;
}
#define rvalue_strong_assign(a, b) (auto ___##b = b, strong_assign(a, &___##b))
好的 - 在这两个示例中,后者可能被认为是“不好的风格” - 不管那意味着什么 - 但双和号是否值得所有麻烦?如果在调用
delete a
之前可能会抛出异常,那仍然不是真正的问题 - 只需设置保护或使用unique_ptr
即可。
编辑[1] 我刚刚意识到对于像std::vector
这样使用动态内存分配并具有高效移动方法的类来说,这是不必要的。这使我之前的想法无效了 - 下面的问题仍然存在。
编辑[2] 如下面评论和答案中讨论的那样,整个重点几乎是没有意义的。应尽可能使用值语义来避免分配开销,因为客户端始终可以将整个内容移至堆上(如果需要)。
std::unique_ptr
这样的类型。(如果没有它,我们仍然会有std::auto_ptr
的问题) - milleniumbug