首先我要说的是,我已经阅读了有关移动语义的许多问题中的一些。这个问题不是关于如何使用移动语义,而是问它的目的是什么 - 如果我没有记错的话,我不明白为什么需要移动语义。
背景
我正在实现一个重磅课程,就这个问题而言,它看起来像这样:
class B;
class A
{
private:
std::array<B, 1000> b;
public:
// ...
}
当需要进行移动分配运算符时,我意识到我可以通过更改b
会员到std::array<B, 1000> *b;
- 那么移动可能只是删除和指针交换。
这让我产生了以下想法:现在,不应该所有非原始类型成员都是加速移动的指针(在下面更正[1] [2])(有一种情况是不应该动态分配内存的,但在这些情况下,优化移动不是问题,因为没有办法这样做)?
这是我有以下认识的地方 - 为什么创建一个类A
它实际上只是容纳一个指针b
所以当我可以简单地创建一个指向整个的指针时,稍后交换会更容易A
类本身。显然,如果客户端期望移动速度明显快于复制,则客户端应该可以接受动态内存分配。但在这种情况下,为什么客户端不直接动态分配整个A
class?
问题
难道客户端不能利用指针来完成移动语义给我们提供的一切吗?如果是这样,那么移动语义的目的是什么?
移动语义:
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
.
Edit [1]我刚刚意识到对于诸如此类的课程来说这是没有必要的std::vector
它们本身使用动态内存分配并具有高效的移动方法。这只是使我的想法无效 - 下面的问题仍然存在。
Edit [2]正如下面的评论和答案中的讨论中所提到的,这一点几乎没有实际意义。人们应该尽可能多地使用值语义来避免分配开销,因为如果需要,客户端总是可以将整个事物移动到堆中。