C++ 和标准容器:成员的内存地址
C++ and standard containers: memory addresses of members
我目前在理解标准容器中的内存管理方面遇到了一些问题,尤其是 std::vector
。
我很清楚,如果没有足够的 space 保留,std::vector
将在向其添加元素后调整大小,因此移动每个元素并更改内存中的地址。我现在的问题是:元素的成员变量发生了什么?
我的问题是基于这样的想法,即对于我的游戏引擎,我目前正在 std::vector
中管理场景。场景由 Scene
管理器 class 管理,其中包含 std::vector
个 Scene
。添加场景如下所示:
std::vector<Scene> scenes;
Scene* active;
...
Scene scene;
scenes.emplace_back(scene);
active = &scenes.back();
场景是堆栈分配的,离开方法后将被丢弃。为了将当前添加的场景暴露给外部,我存储了一个指向 std::vector
后面的指针,这是新插入的元素。
Scene
s 包含各种成员,例如 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;
}
};
或者,您可以通过将复制构造函数定义为 = delete
d
来禁止复制 Scene
s
class UnCopyable
{
int* ptr;
public:
UnCopyable() : ptr(new int) {}
~UnCopyable() { delete ptr; }
UnCopyable(const UnCopyable & other) = delete;
UnCopyable& operator=(const UnCopyable & other) = delete;
};
我目前在理解标准容器中的内存管理方面遇到了一些问题,尤其是 std::vector
。
我很清楚,如果没有足够的 space 保留,std::vector
将在向其添加元素后调整大小,因此移动每个元素并更改内存中的地址。我现在的问题是:元素的成员变量发生了什么?
我的问题是基于这样的想法,即对于我的游戏引擎,我目前正在 std::vector
中管理场景。场景由 Scene
管理器 class 管理,其中包含 std::vector
个 Scene
。添加场景如下所示:
std::vector<Scene> scenes;
Scene* active;
...
Scene scene;
scenes.emplace_back(scene);
active = &scenes.back();
场景是堆栈分配的,离开方法后将被丢弃。为了将当前添加的场景暴露给外部,我存储了一个指向 std::vector
后面的指针,这是新插入的元素。
Scene
s 包含各种成员,例如 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;
}
};
或者,您可以通过将复制构造函数定义为 = delete
d
Scene
s
class UnCopyable
{
int* ptr;
public:
UnCopyable() : ptr(new int) {}
~UnCopyable() { delete ptr; }
UnCopyable(const UnCopyable & other) = delete;
UnCopyable& operator=(const UnCopyable & other) = delete;
};