【发布时间】:2013-12-16 19:43:24
【问题描述】:
让我先说我已经阅读了一些关于移动语义的问题。这个问题不是关于如何使用移动语义,而是问它的目的是什么——如果我没记错的话,我不明白为什么需要移动语义。
背景
我正在实现一个重型类,就这个问题而言,它看起来像这样:
class B;
class A
{
private:
std::array<B, 1000> b;
public:
// ...
}
当需要进行移动赋值运算符时,我意识到可以通过将b 成员更改为std::array<B, 1000> *b; 来显着优化该过程 - 然后移动可能只是删除和指针交换。
这让我想到了以下想法:现在,不应该所有非原始类型成员都是加速移动的指针(在[1] [2]下方更正)(有一个案例用于不应该动态分配内存的情况,但在这些情况下优化移动不是问题,因为没有办法这样做)?
在这里我有以下认识——为什么要创建一个类A,它实际上只包含一个指针b,所以当我可以简单地创建一个指向整个A 类本身的指针时,以后交换会更容易。显然,如果客户端期望移动比复制快得多,客户端应该可以接受动态内存分配。但是在这种情况下,为什么客户端不只是动态分配整个A类呢?
问题
客户端不能利用指针来完成移动语义给我们的所有操作吗?如果是这样,那么移动语义的目的是什么?
移动语义:
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] 正如下面 cmets 和答案中的讨论所述,这一点几乎没有实际意义。应该尽可能地使用值语义来避免分配开销,因为如果需要,客户端总是可以将整个东西移动到堆中。
【问题讨论】:
-
为什么要有乘法?你可以用加法做同样的事情。
-
注意:移动语义的第二个原因是像
std::unique_ptr这样的类型可以实现。 (没有它,我们仍然会有std::auto_ptrsuckage) -
@VF1 我们可以用加法和while循环来做乘法,不是吗?
-
类的一个主要目的是管理资源。他们通过封装来简化您在此处显示的指针事物。当然,您可以使用大型数组成员创建值语义类型;那么您可以直接使用它,也可以将其放入一个类中,该类在免费商店中管理它的创建和删除。对于后者,移动语义很有用。
-
这不是有点像在问:既然 C 也能做到这一切,为什么还要 C++?
标签: c++ c++11 move-semantics