自定义中的移动与复制性能 类

Move vs copy performance in custom classes

如果 class 的成员主要是基本类型,如 int等等 那些成员不是像在复制 ctor 中一样被复制了吗?那么在处理自定义 classes 时,move 何时提供更好的性能?

How (if at all) does creating an instance of a class via move ctor improve performance compared to the copy ctor

你误会了。通常,您不会选择对象是用移动构造函数还是复制构造函数构造的——它隐含在其定义的上下文中。所以似乎没有太多选择。当然,如果移动构造函数不可用,那么复制构造是一个后备方案。不过,不要把它当作一个选择。

if the members of said class are mainly basic types like int etc. aren't those members just copied like in the copy ctor?

它们可能根本不会被复制,即编译器可能会完全忽略对象的构造并只使用您传递的对象。但是如果构造真的发生了,那么是的,ints 等将被复制(假设默认移动 ctor)。

So when does move provide better performance, when dealing with custom classes?

再说一次,你想错了。移动构造函数用于移动,复制构造函数用于复制。对于某些 类 移动构造函数可能更快(例如,如果您可以切换分配的缓冲区等);对于某些人来说不是。

延伸阅读:Reasonable presentation on these two kinds of ctors on Andrzej's C++ blog.

How (if at all) does creating an instance of a class via move ctor improve performance, compared to the copy ctor, if the members of said class are mainly basic types like int etc.

没有。

Aren't those members just copied like in the copy ctor?

是的。

So when does move provide better performance, when dealing with custom classes?

当您拥有 "indirect" 资源(如指针引用的数据)时,您可以只 "swap" 指针并避免接触它们指向的内容。 (这就像从您的汽车上取下一辆大篷车,然后将其重新挂在另一辆汽车上。)就是这样。

如果 class 仅包含无论如何都必须复制的数据,则将其设为可移动没有任何好处。 (这就像试图 "move" 将一辆大篷车的物品转移到另一辆大篷车:你别无选择,只能真正进去,把所有东西都捡起来,然后用手转移。)

How (if at all) does creating an instance of a class via move ctor improve performance, compared to the copy ctor, if the members of said class are mainly basic types like int etc. aren't those members just copied like in the copy ctor?

在所有成员变量都是by-value/POD的情况下,应该没有任何区别。

So when does move provide better performance, when dealing with custom classes?

移动构造函数仅在新构造的对象可以"steal" 已存在对象的资源的情况下提供优势。

例如,假设您有一个临时 std::string,其中包含小说 'War and Peace' 的全部内容 -- 全部 1440 页。

在 classic 复制构造函数的情况下,如果您想将该临时字符串分配给非临时字符串 std::string(例如,成员变量或全局变量或其他),该程序必须执行以下步骤:

  • 释放目标 std::string 可能持有的任何先前缓冲区
  • 分配一个长度为 (1440*chars_per_page) 字节的新缓冲区,供目标 std::string 保存
  • 将所有 1440 页数据从临时 std::string 的缓冲区复制到目标 std::string 的缓冲区
  • 删除临时字符串的缓冲区(当临时字符串超出范围时)

如您所见,这将是低效的,因为我们复制了大量数据,即使我们实际上并不需要数据的第二个副本。由于我们为 std::string 实现了一个移动构造函数,因此 C++11 程序可以更智能,只需这样做:

  • 释放目标 std::string 可能持有的任何先前缓冲区
  • 将巨型缓冲区从临时 std::string 转移到目标 std::string (请注意,我们所做的只是将源字符串的指向缓冲区的指针值复制到目标字符串;特别是我们不需要复制,甚至不需要读取任何实际的 1440 页数据!)
  • 将临时字符串的指向缓冲区的指针值设置为 NULL(因此它不会尝试释放它或稍后使用它)

...就是这样;我们不必分配第二个大缓冲区然后复制数据块,只需通过混合几个指针值就可以实现所需的最终状态。这是一个巨大的性能胜利,我们可以逃脱这样做,因为我们知道临时字符串无论如何都会被删除,所以 "stealing" 它所持有的内部数据没有任何危害。

我不知道 std::string 是否算作 'custom class',但是你可以在你自己的 classes 中使用相同的技术,只要你有 [=63] =] 动态分配内部状态。