C++ 堆/栈澄清

C++ heap / stack clarification

我需要一些关于 C++ 内存分配的说明,我直接举个例子

假设我制作了一个 class A,其中包含两个容器:一个 hash_map 和一个 std::vector,如下所示:

class Example{

// methods to add stuff to containers
//...

std::hash_map<std::string,int> map;
std::vector<std::string> vec;
}

如果我然后使用 new 运算符在堆上创建一个 example 对象:

Example* ex = new Example();

并向每个容器添加一千个条目,我添加的条目是否也会位于堆中?如果是,那么如果我这样做会有什么不同:

 class Example{

    // methods to add stuff to containers
    //...

    std::hash_map<std::string,int>* map;
    std::vector<std::string>* vec;
    }

然后是Example* ex = new Example();

无论 hash_mapvector 存储在何处,它们包含的数据将始终在堆中。

示例 1

int main() {
    hash_map<T> table;
}

table 在堆栈上。 table 包含的数据在堆上。

示例 2

int main() {
    hash_map<T> *table = new hash_map<T>();
}

table是一个存在于栈上的指针。 *table 是堆上存在的 hash_map*table 包含的数据仍在堆上。

示例 3

hash_map<T> table;
int main() {
    ...
}

table 不在堆或堆栈中。 table指向的数据还在堆上。

new Example(); 该语句总是意味着在堆上分配一个 Example 对象和 return 指向该对象的指针。

Example ex;是在栈上分配单个Example对象的方法。

您描述的不同之处在于 Example 对象的内容。在第一个实例中,对象直接持有 stl 向量和 hash_map 的实例(它们是用对象构造并随对象一起释放的)

在第二种情况下,对象持有容器指针,而您放置在这些指针上的内容保持打开状态。

在任何情况下,vector 和 hash_map 都会使用堆来存储您插入其中的数据,但是它们向您隐藏了细节和管理。 (您可以提供分配器来破坏默认值)

高级说明: Example ex; 当在函数/方法的范围内时,将在堆栈上分配对象。当作用域退出时,对象被释放。当在命名空间或全局范围内使用时,它将在数据段中分配对象,并在程序终止时释放该对象。

两个容器中的数据项将始终在堆中; 拥有 std::hash_map 地图和 std::hash_map *map 是,在第一种情况下,您的 Example 对象包含实际的对象映射(特别是,当 Example 对象被销毁时,它会自动销毁),而在第二种情况下,您的 Example 对象仅包含指向的指针对象映射(在其他地方可能是 created/destroyed)。

在这两种情况下,您的对象都将在堆上实例化。 new 运算符就是这样做的。

你的两个例子之间的区别在于,在第一个例子中,两个成员没有使用 *,当你调用 new Example() 时,它们被隐式构造为你的对象的一部分(在内存中,您的对象布局将包含一个 hash_map 和一个大小为 sizeof(hash_map)sizeof(vector)) 的矢量。

在第二种情况下,当您将成员声明为指针时,内存中的对象布局将仅包含 2 sizeof(void*) - 基本上是指针的大小,所有指针的大小都相等,无论它们是什么类型指向。这也意味着,当您使用 new Example() 实例化 class 时,默认情况下您的成员将是 nullptr,因此您必须使用 new hash_mapnew vector 以便使用它们。

至于堆栈分配,如果你想在堆栈上分配你的 Example class 的实例,你可以在函数中使用

Example foo;

而不是

Example* foo = new Example();

这将在堆栈上分配 foo 并在函数执行时自动销毁它 returns。