C++ 和标准容器:成员的内存地址

C++ and standard containers: memory addresses of members

我目前在理解标准容器中的内存管理方面遇到了一些问题,尤其是 std::vector

我很清楚,如果没有足够的 space 保留,std::vector 将在向其添加元素后调整大小,因此移动每个元素并更改内存中的地址。我现在的问题是:元素的成员变量发生了什么?

我的问题是基于这样的想法,即对于我的游戏引擎,我目前正在 std::vector 中管理场景。场景由 Scene 管理器 class 管理,其中包含 std::vectorScene。添加场景如下所示:

    std::vector<Scene> scenes;
    Scene* active;
...
        Scene scene;
        scenes.emplace_back(scene);
        active = &scenes.back();

场景是堆栈分配的,离开方法后将被丢弃。为了将当前添加的场景暴露给外部,我存储了一个指向 std::vector 后面的指针,这是新插入的元素。

Scenes 包含各种成员,例如 Light class 的实例。出于各种原因,我还向外部公开了指向这些元素的指针。我的问题是我试图在 Scene 的构造函数中使用这些指针,这些指针是在 Scene 管理器中构造的。将对象添加到 std::vector 后,似乎构造了一个新对象,即使 Scene 构造函数似乎没有被调用。 "active" 成员现在包含的内存地址与我之前分配的实际 Scene 对象不同。由于矢量需要调整大小,我很清楚我的想法。

但是现场的成员会怎样呢?原来的Scene会被破坏,活动元素得到另一个内存地址。这意味着它指向一个全新的元素,因为内部调整大小并且新元素有新成员,在我的例子中是我想要使用的那些。

我的理解对吗?

我的第二个问题包括我应该如何处理这样的情况:我想公开指向存储在 std::vector 中且大小未知的对象成员的指针。我目前选择的方法很好用,但我不确定这是否是正确的方法:

Scene class 中有一个 onActivate 事件方法,它将在整个调整大小和获取向量的插入元素完成后调用。当我切换活动指针时,该方法也会被调用。该方法获取指向场景成员的指针并将它们四处传递。它看起来像这样:

void postConstruct() {
    std::cout << "postConstruct: " << &el << std::endl;
}

并将在 Scene 管理器中的正确位置调用,该管理器目前是 Scene class 的 friend,因为这些事件不应暴露给外面的世界。

    active->postConstruct();

这是正确的方法吗?

如果 std::vector 被调整大小,如果移动构造函数声明为 noexcept,元素将使用元素的移动构造函数移动,或者将使用元素的复制构造函数复制到新元素分配的位置。

重新分配后成员指针是否相同将取决于如何为插入的元素实现移动构造函数或复制构造函数。

我建议使用索引而不是 Scene* 来访问 std::vector 中的元素,或者如果您想使用 Scene*

则使用 std::list

展开vector时,所有迭代器、指针和对元素的引用都将失效。 只有定义了你可以用一个无效的指针或迭代器做的事情是用另一个值覆盖它,并且有没有你可以用一个无效的参考。即使 将值与其他值进行比较 也会使您的程序 格式错误 .

当你

Scene scene; 
scenes.emplace_back(scene); 
active = &scenes.back();

您有 两个 Scene 个对象。一个是局部变量,另一个在向量中,并且是 scene 复制。我不确定您是否意识到这种区别,您可能只想要 one Scene 对象。要么所有 Scene 都住在 scenes 中,要么将其更改为 std::vector<Scene *>std::vector<std::reference_wrapper<Scene>>。如果您执行后者,请确保在销毁之前删除值。

语言提供的复制构造函数将简单地复制每个成员的,在指针的情况下,这往往是错误的。您可以为 Scene 显式定义一个复制构造函数来准确控制发生的事情,例如"deep copy" 指针成员。

class Copyable
{
    int* ptr;
public: 
    Copyable() : ptr(new int) {}
    ~Copyable() { delete ptr; }
    Copyable(const Copyable & other) : ptr(new int(*other.ptr)) {} // deep copy
    Copyable& operator=(const Copyable & other)
    {
        *ptr = *other.ptr;
        return *this;
    }
};

或者,您可以通过将复制构造函数定义为 = deleted

来禁止复制 Scenes
class UnCopyable
{
    int* ptr;
public: 
    UnCopyable() : ptr(new int) {}
    ~UnCopyable() { delete ptr; }
    UnCopyable(const UnCopyable & other) = delete;
    UnCopyable& operator=(const UnCopyable & other) = delete;
};