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_map
或 vector
存储在何处,它们包含的数据将始终在堆中。
示例 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_map
和 new vector
以便使用它们。
至于堆栈分配,如果你想在堆栈上分配你的 Example
class 的实例,你可以在函数中使用
Example foo;
而不是
Example* foo = new Example();
这将在堆栈上分配 foo 并在函数执行时自动销毁它 returns。
我需要一些关于 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_map
或 vector
存储在何处,它们包含的数据将始终在堆中。
示例 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_map
和 new vector
以便使用它们。
至于堆栈分配,如果你想在堆栈上分配你的 Example
class 的实例,你可以在函数中使用
Example foo;
而不是
Example* foo = new Example();
这将在堆栈上分配 foo 并在函数执行时自动销毁它 returns。