【问题标题】:Passing ownership of stack object without duplication传递堆栈对象的所有权而不重复
【发布时间】:2020-02-27 21:43:45
【问题描述】:

第三方库具有 API Huge computeHuge()。它返回对象本身,而不是引用/指针。我无法控制对象或 API。

我有两个课程:

class Bar {
  Huge h;
  Bar(Huge &huge): h(huge);
}

class Foo {
  Bar b;

  Foo() {
    Huge h = computeHuge();
    b = Bar(h);
  }

不幸的是,这种设计(暂时)导致了一个巨大对象的两个副本:一个副本存在于Foo 构造函数中,另一个存在于Bar 对象中。一旦Foo 构造函数退出,只有一个副本,但我需要构造函数内的内存加倍。由于h 可能有数百 GB,这很重要。

解决这个问题的一个方法是让Foo成为h的所有者:

class Bar {
  Huge &h;
  Bar(Huge &huge): h(huge);
}

class Foo {
  Bar b;
  Huge h;

  Foo() {
    h = computeHuge();
    b = Bar(h);
  }

这确实成功地消除了h 的两个副本,但在我的应用程序中并没有真正意义:Bar 是持有h 的正确事物。 我该怎么做:

  1. Foo 构造函数中调用computeHuge()
  2. Bar 保留h 的所有权
  3. 所有没有在内存中需要h 的两个副本?

【问题讨论】:

  • 我不相信堆栈上可以有数百 GB。通常,默认堆栈大小为 1 MB。我认为h 内部数据是动态分配的(假设您有足够大的 RAM 内存来存储数百 GB,我对此表示严重怀疑)。但是,如果您可以访问Huge 的内部数据,您或许可以持有指向它的指针。
  • 是的。 Huge 是一个非常大的向量的包装器,这个向量大概是在堆上分配的。但我无法控制它,因为Foo() 中的Huge 对象将在构造函数退出时被清理,这将删除向量。即使我可以访问内部向量,我也无法阻止它在构造函数退出时被删除。我正在使用一个 非常 大的 EC2 实例,所以有足够的内存 :)
  • 根据给定答案中的建议,您可以移动您的std::vector(如果您不能直接移动Huge,这是首选)。它应该可以解决问题:)
  • ^ 如果不是这样:让 Bar 生成(空)Huge 对象,然后在 Bar 中有一个 get-huge-reference 函数,然后用它来填充 Foo 中的对象?像图像库中的 get-image-buffer 类型函数?
  • @DaveS 你能把它写下来作为答案吗?

标签: c++ memory-management shared-memory


【解决方案1】:

如果Huge 是可移动的,则不会生成任何副本:

class Bar {
  Huge h;
  Bar(Huge huge): h(std::move(huge)) {}   // move huge into its final place, h
};

class Foo {
  Bar b;

  Foo() {
    Huge h = computeHuge();
    b = Bar(std::move(h));   // move h into constructor argument
    // h cannot be used here anymore
  }
};

出于调试目的,这是一个(很小的)Huge,不能复制,只能移动。每次尝试复制都是编译器错误:

struct Huge {
    Huge() = default;
    Huge(Huge&& other) { std::cout << "move "; }
    Huge(const Huge& other) = delete;
    Huge& operator=(Huge&& other) { std::cout << "move= "; return *this; }
    Huge& operator=(const Huge& other) = delete;
};

【讨论】:

  • 我尝试了你的建议,但我得到了与以前相同的行为:双倍内存使用。也许这意味着我的对象不可移动?
  • 其实我只是在阅读this,结果发现不可移动的类型非常罕见,在某些情况下甚至不能从函数中返回,所以Huge 是可能是可移动的。除非它已明确设为不可移动,否则这是没有意义的。我玩的是不可移动的here。我认为其他地方仍在制作副本。因为正如您在上一个链接中看到的那样,这里的代码没有复制。
  • @crockeea 你能改一下Huge吗?如果是的话我会=deletecopy-constructor 和copy-operator=,那么每个副本都是编译错误。
  • 不,它在外部库中。
  • 好的,那我会做一个假的Huge(更新答案)来看看副本是在哪里制作的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-04
  • 2019-11-08
  • 1970-01-01
  • 1970-01-01
  • 2013-08-03
  • 1970-01-01
  • 2011-11-25
相关资源
最近更新 更多