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

时间:2018-07-11 11:58:43

标签: c++ c++11 pointers vector stdvector

我目前对标准容器(尤其是std::vector)中的内存管理有一些了解。

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

我的问题基于这样一个想法:我目前正在为我的游戏引擎管理std::vector中的场景。场景由Scenestd::vector的{​​{1}}管理器类管理。添加场景如下所示:

Scene

场景是堆栈分配的,离开该方法后将被丢弃。为了将当前添加的场景暴露在外面,我要存储指向 std::vector<Scene> scenes; Scene* active; ... Scene scene; scenes.emplace_back(scene); active = &scenes.back(); 后面的指针,该指针是新插入的元素。

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

但是现场成员会怎样?原始的Scene将被破坏,活动元素获得另一个内存地址。这意味着由于内部调整大小,它指向一个全新的元素,并且该新元素具有新成员,在我的情况下,这些成员是我要使用的成员。

我的理解正确吗?

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

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

onActivate

并且将在void postConstruct() { std::cout << "postConstruct: " << &el << std::endl; } 管理器中的正确位置被调用,该管理器当前是Scene类的friend,因为这些事件不应暴露给外部世界。

Scene

这是正确的方法吗?

2 个答案:

答案 0 :(得分:2)

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

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

我建议使用比Scene*更高的索引来访问std::vector中的元素,或者如果您想使用std::list则使用Scene*

答案 1 :(得分:1)

展开vector时,所有元素的迭代器,指针和引用均无效。您可以使用无效的指针或迭代器执行的定义的操作将用另一个值覆盖它,并且您无法使用无效的引用进行任何操作。甚至将该值与其他值进行比较,也会使您的程序形成错误的格式。

完成时

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

您有两个 Scene个对象。一个是局部变量,另一个是向量中的变量,并且是从scene复制而来的。我不确定您是否意识到这种区别,您可能只希望一个 Scene对象。所有Scene都生活在scenes中,或者将其更改为std::vector<Scene *>std::vector<std::reference_wrapper<Scene>>。如果要进行后者处理,请确保在值被销毁之前将其删除。

提供的语言复制构造函数将简单地复制每个成员的 value ,对于指针,这往往是错误的事情。您可以为Scene明确定义一个复制构造函数,以精确控制发生的情况,例如“深层复制”指针成员。

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;
    }
};

或者,您可以通过将复制构造函数定义为Scene d来禁止复制= delete

class UnCopyable
{
    int* ptr;
public: 
    UnCopyable() : ptr(new int) {}
    ~UnCopyable() { delete ptr; }
    UnCopyable(const UnCopyable & other) = delete;
    UnCopyable& operator=(const UnCopyable & other) = delete;
};