【问题标题】:Is there a way to assign a stacked object to the allocated memory using placement new?有没有办法使用placement new 将堆叠对象分配给分配的内存?
【发布时间】:2019-04-04 12:42:31
【问题描述】:

这是我的程序:

#include <iostream>

using namespace std;

class Object {
public:
    Object() { cout << "Object constructor!" << endl; }
    ~Object() { cout << "Object destructor!" << endl; }
    Object(const Object& obj) { information = obj.information; cout << "Copy constructor!" << endl; }
    void setInformation(const string info) { information = info; }
    string getInformation() const { return information; }
private:
    string information;
};

class Storage {
public:
    Storage() { object = static_cast<Object*>(operator new(sizeof(Object))); }
    ~Storage() { operator delete(object); }

    void setObject(const Object& obj) {
        // Todo: assign obj to the allocated space of the pointer object
    }
private:
    Object* object;
};

int main()
{
    Object o;
    o.setInformation("Engine");
    Storage storage;
    storage.setObject(o);
    return 0;
}

在存储中,我分配空间来存储一个 Object 类型的对象而不创建它。我正在使用placement new来分配内存,并在析构函数中释放它。我知道我可以使用

object = new(object) Object()

构造一个对象。但是我可以在内存中放入一个已经创建的对象吗?就我而言,调用方法 setObject()。如果是的话,我会遇到这样的内存管理什么问题?提前致谢。

【问题讨论】:

  • 这里的问题很多。除了Storage 本身违反了三规则之外,如果setObject 从未被调用,则析构函数会尝试delete 一些不是作为对象实例构造的东西。卡布姆。
  • 您需要在某处分配您的对象,并且没有理由将来自setObject 的对象分配在堆栈上,并且您无法检索它,因为它是常量。所以你不能把它分配给你的指针。使用 unique_ptr 存储对象并在调用 setObject 时创建一个新对象。如果您不想创建新的 obe,请传入 unique_ptr。明确说明您的内存管理。
  • Sam,我知道,但感谢您指出这一点。该程序尚未完成。这就是为什么我决定先问具体问题:如何填充方法 setObject()。我知道我以后必须显式调用析构函数。
  • 你仅限于 c++11 吗?
  • 不,我不受限制。据我了解,这不能在 C++ 11 中完成,对吧?

标签: c++ c++11 reference placement-new


【解决方案1】:

实现您尝试实现的行为的最简单方法是使用 C++17 中的std::optional。它将允许您为您的Object“保留”内存,而无需构造它并且不使用堆。它还将处理所有的构造函数和析构函数调用。

这也可以在没有std::optional 的情况下完成,但您基本上必须自己实施类似的解决方案。在任何情况下,您都需要一个额外的成员来指定对象是否构造,以便您可以正确处理销毁和赋值。

那么您的setObject 方法将如下所示:

void setObject(const Object& obj) {
    if (constructed) {
        *object = obj;
    } else {
        new (object) Object(obj);
        constructed = true;
    }
}

要使用移动语义,您可以像这样修改您的方法:

void setObject(Object obj) {
    if (constructed) {
        *object = std::move(obj);
    } else {
        new (object) Object(std::move(obj));
        constructed = true;
    }
}

请注意,现在setObject 按值接受它,因此它可以与左值和右值引用一起使用来构造参数,然后将其移动到成员中。

【讨论】:

  • 谢谢,是的,我以后应该这样做。
  • @Oleg,使用std::optional 会更好,但如果你想自己做,你可以使用缓冲区char buffer[sizeof(Object)] 或更优雅的解决方案来摆脱动态分配将使用一个成员union { Object obj; }
  • 我是否更正了如果我使用你写的 setObject 我需要做的下一步: if (constructed) (*object).~Object();在析构函数中?
  • @Oleg,是的,您可以使用object-&gt;~Object(); 以获得更好的可读性。
  • 如果复制构造函数被删除,有没有办法做同样的事情?
【解决方案2】:

Placement new 是一个相当高级的构造,想到有一个实际上指向未初始化内存的Object* 是相当可怕的。我可以建议你让你的生活更轻松吗?

class Storage {
    void setObject(const Object& obj) {
        if (object) {
            *object = obj;
        } else {
            object.reset(new Object(obj));
        }
    }
private:
    std::unique_ptr<Object> object;
};

这需要Object 类上的复制构造函数和赋值运算符,但更符合 C++ 习惯。而在如图所示的Object 实现中,编译器提供的默认实现已经可以了。

【讨论】:

    【解决方案3】:

    澄清一下:

    但是我可以将一个已经创建的对象放入内存吗?

    不,你不能。没有办法在 C++ 中移动内存中的对象。对于普通可复制类型,您可以复制(例如,通过memcpymemmove)它们的内存表示。但这实际上并没有移动任何东西。此外,您的 Object 类不可轻易复制。

    你可以移动的是对象的内容,这就是移动语义的作用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-01
      • 2012-04-24
      • 2023-01-01
      • 2020-12-19
      • 2011-06-17
      相关资源
      最近更新 更多