4

我有一个类,它包含一个指向一大块已分配内存和许多原始类型成员的指针。我正在考虑移动构造函数,并认为这是使用它的绝佳机会。显然,指针应该被移过去,但如果使用原语是个好主意,那就别想了。

下面是该类的一个人为示例:

class Foo {
private:
  long m_bar = 1;
  /* 20+ similar members */
};

为了使它们可移动,它们必须被动态分配。

class Foo {
public:
  Foo(Foo && rhs) : m_bar(rhs.m_bar) { rhs.m_bar = nullptr; }
  ~Foo() { delete m_bar; }
private:
  long *m_bar = new long{1};
};

我的问题是,在堆上分配的开销是否会抵消移动语义引入的性能提升?

4

3 回答 3

6

如果有的话,我相信像这样分配每个成员的堆最终会变慢。在最初的堆分配之上,仅在构造时执行,持有指向堆上许多小的、不连续的数据成员的指针并不能很好地与 CPU 缓存策略配合使用。

一些类移动得很好,因为它们有大量的堆分配内存(例如 std::string)。在您的情况下,移动每个指针将与移动较小的数据类型一样昂贵。我可以看到这更快的唯一方法是,如果您将较小的数据成员包装在堆分配的类/结构中(可能持有指向它的唯一指针。)并通过单个指针的移动来移动所有这些成员。

也就是说,这很可能是过早优化的情况。您可能希望让代码按原样工作,并确定为您的类实现更复杂的移动语义确实可以真正帮助您的代码性能。

于 2012-11-19T15:19:46.007 回答
2

只有当移动对象比复制对象更快时,移动语义才会更快。在你的例子中,这不是真的。复制 long 应该与将指针复制到 long 的速度相同。通过动态分配每个单独的成员来添加移动语义几乎肯定会减慢速度,而不是加快速度。

使用 PIMPL 习惯用法可能会导致更快的移动构造函数。您将动态分配一个包含所有成员的类,主类将仅包含指向该类的指针。然后,您的移动构造函数所要做的就是将指针复制到实现类。

于 2012-11-20T17:33:46.907 回答
1

如果原语既不比指针大,也不(以某种方式)复制起来比指针贵,那么动态分配它只是额外的成本。

您可能希望使原件无效,但这不需要指针。

于 2012-11-19T15:11:28.853 回答