将 emplace_back 对象转换为 std::list 的正确方法

Proper way to emplace_back object into std::list

我有一个 class,其中包含各种成员(主要是整数、浮点数和一些动态容器)。我有一个 std::list 我的 class 对象。在此 class 的构造函数中,在设置所有参数后调用

myclassobjects.emplace_back(*this)

这行得通并且似乎不会导致任何分配问题;我从来没有 sigsegv 问题或类似问题。然而,我并不完全相信这是正确的做法,或者它不会导致缓慢的内存泄漏。

这种方法使用安全吗,会不会导致内存泄漏?如果是,'proper' 构建对象并立即将其放入 std::list 中的方法是什么?

确实不可能将您的对象存储在 std::list 或任何其他容器中。根据对象的标准定义,对象是 存储区域 。一个存储区域不能存储,它只是

假设 X 是一个现有对象。容器不能存储 X 本身,而是以某种方式与 X 相关的值。有两种选择。

  1. 存储 X 的 副本
  2. 将(智能)指针或引用包装器存储到 X。

由于您正在部署 *this,因此您正在存储一个副本。这可能是也可能不是您需要的。

要存储裸指针,您必须emplace_back(this),当然还要更改列表的类型。这是危险的,因为当 X 不复存在时,您的列表可能仍然有一个指向它的悬空指针。你需要确保它不会发生。

您可能要考虑存储共享指针,但如果您想通过继承 enable_shared_from_this 来启用它,请注意

首先emplace_back通过转发参数就地构造一个对象。所以你最终做的是用参数 *this.

调用你的 class 的复制构造函数

我正在尝试考虑需要将对象添加到构造函数内的列表的情况,但我遇到了困难。我认为有更好的选择。

从构造函数外部将其放置在列表中

简单创建如下。

myclassobjects.emplace_back(/*constructor params*/);

在 C++17 中,这甚至 return 是对新创建对象的引用

MyClass& myclass_ref = myclassobjects.emplace_back(/*constructor params*/);
do_something_with_my_class_object(myclass_ref);

这是最干净、最有效的方法。它还有一个额外的好处,即您可以创建本地对象,而无需在需要时将它们添加到列表中。

使用工厂方法将其添加到列表中

如果您绝对必须拥有列表中的每个对象并且您不希望它是一个副本,请使用静态工厂方法。如果列表和被调用者必须共享所有权,您可以使用 std::shared_ptr 的列表并执行以下操作。

MyClass {
private:
    MyClass();    // private constructor forbids on the stack variables

public:
    static std::shared_ptr<MyClass> create_instance() {
        auto ptr = make_shared<MyClass>();    // We can access the constructor here
        myclass_objects.emplace_back(ptr);
        return ptr;
    }
}

另一方面,如果你的列表保证比对象上的被调用者的处理程序长寿,那么拥有一个包含对象和 return 引用的 std::unique_ptr 列表更合适给他们。

MyClass {
private:
    MyClass();    // private constructor forbids on the stack variables

public:
    // pre C++17
    static MyClass& create_instance() {
        auto ptr = make_unique<MyClass>();
        auto& ref = *ptr;    // Store ref before we move pointer
        myclass_objects.emplace_back(std::move(ptr));
        return ref;
    }

    // C++17
    static MyClass& create_instance() {
        auto ptr = make_unique<MyClass>();
        return *(myclass_objects.emplace_back(std::move(ptr)));
    }
}

当然,这些示例仅涵盖默认构造函数。因为您通常需要使用更多的构造函数,所以您可能需要一个模板化的 create_instance 将其参数转发给构造函数。