C++动态分配的内存
C++ dynamically allocated memory
我不太明白动态分配内存的意义,我希望你们能为我说得更清楚。
首先,每次分配内存时,我们都会得到一个指向该内存的指针。
int * dynInt = new int;
那么做我上面做的事情和:
int someInt;
int* dynInt = &someInt;
据我了解,在这两种情况下,内存都是为 int 分配的,我们得到一个指向该内存的指针。
那么两者之间有什么区别。什么时候 一种方法优于另一种方法。
此外,为什么我需要释放内存
delete dynInt;
在第一种情况下,但在第二种情况下则不然。
我的猜测是:
为对象动态分配内存时,对象不会被初始化,而如果你像在第二种情况下那样做一些事情,对象就会被初始化。如果这是唯一的区别,那么除了动态分配内存更快这一事实之外,这背后是否有任何动机。
对于第二种情况,我们不需要使用 delete 的原因是,对象被初始化的事实创建了某种自动销毁例程。
这些只是猜测,如果有人纠正我并为我澄清事情,他们会喜欢它。
区别在于存储持续时间。
-
具有自动存储持续时间的对象是您的"普通"对象,它们在定义它们的块结束时会自动超出范围。
像
int someInt
一样创建它们;您可能听说过它们是"堆栈对象",尽管我反对这个术语。
具有 动态存储持续时间的对象具有某种"手动"生命周期; 您必须自己销毁它们
delete
,并使用关键字new
创建它们。您可能听说过它们是"堆对象",尽管我也反对这样做。
指针的使用实际上与它们中的任何一个都没有严格关系。您可以有一个指向自动存储持续时间对象的指针(您的第二个示例(,也可以有一个指向动态存储持续时间对象的指针(您的第一个示例(。
但是,您很少需要指向自动对象的指针,因为:
- 您没有"默认"> ;
- 该对象不会持续很长时间,因此您可以使用这样的指针做很多事情。
相比之下,动态对象通常通过指针访问,这仅仅是因为语法接近于强制执行它。 new
返回一个指针供您使用,您必须将指针传递给delete
,并且(除了使用引用(实际上没有其他方法可以访问该对象。它生活在"外面"的动态云中,而不是坐在局部范围内。
正因为如此,指针的使用有时会与动态存储的使用混淆,但实际上前者与后者没有因果关系。
像这样创建的对象:
int foo;
具有自动存储持续时间 - 对象一直存在,直到变量foo
超出范围。这意味着在您的第一个示例中,一旦someInt
超出范围(例如,在函数末尾(,dynInt
将成为无效指针。
像这样创建的对象:
int foo* = new int;
具有动态存储持续时间 - 对象一直存在,直到您对其显式调用delete
。
对象的初始化是一个正交概念;它与您使用的存储持续时间类型没有直接关系。有关初始化的更多信息,请参阅此处。
-
程序在启动时获得初始内存块。此内存称为堆栈。如今,该数量通常在2MB左右。
-
程序可以要求操作系统提供额外的内存。这称为动态内存分配。这会在免费存储(C++术语(或堆(C 术语(上分配内存。您可以要求系统愿意提供的内存量(数千兆字节(。
在堆栈上分配变量的语法如下所示:
{
int a; // allocate on the stack
} // automatic cleanup on scope exit
使用免费存储中的内存分配变量的语法如下所示:
int * a = new int; // ask OS memory for storing an int
delete a; // user is responsible for deleting the object
要回答您的问题:
什么时候 一种方法优于另一种方法。
- 通常首选堆栈分配。
- 当您需要使用其基类型存储多态对象时,需要动态分配。
- 始终使用智能指针自动删除:
- C++03:
boost::scoped_ptr
、boost::shared_ptr
或std::auto_ptr
。 - C++11:
std::unique_ptr
或std::shared_ptr
。
- C++03:
例如:
// stack allocation (safe)
Circle c;
// heap allocation (unsafe)
Shape * shape = new Circle;
delete shape;
// heap allocation with smart pointers (safe)
std::unique_ptr<Shape> shape(new Circle);
此外,为什么在第一种情况下我需要释放内存,但在第二种情况下不需要。
正如我上面提到的,堆栈分配的变量在范围退出时自动释放。请注意,不允许删除堆栈内存。这样做将不可避免地使您的应用程序崩溃。
对于单个整数,只有在您需要在例如从函数返回之后保留值时才有意义。如果你像你说的那样宣布someInt
,一旦超出范围,它就会失效。
但是,一般来说,动态分配的用途更大。在分配之前,您的程序不知道很多事情,并且取决于输入。例如,程序需要读取图像文件。该图像文件有多大?我们可以说我们将其存储在这样的数组中:
unsigned char data[1000000];
但这仅在图像大小小于或等于 1000000 字节时才有效,并且对于较小的图像也会浪费。相反,我们可以动态分配内存:
unsigned char* data = new unsigned char[file_size];
在这里,file_size
是在运行时确定的。在编译时,您不可能分辨出此值。
阅读有关动态内存分配和垃圾回收的更多信息
你真的需要阅读一本好的C或C++编程书籍。
详细解释会花费很多时间。
堆是发生动态分配(new
在 C++ 中或malloc
在 C 中(发生的内存。存在与增加和缩小堆有关的系统调用。在Linux上,它们是mmap和munmap(用于实现malloc
和new
等(。
您可以多次调用分配原语。因此,您可以将int *p = new int;
放在循环中,并在每次循环时获得一个新的位置!
不要忘记释放内存(delete
在C++中或free
在C中(。否则,你会得到一个内存泄漏 - 一种顽皮的错误 - 。在Linux上,valgrind有助于抓住它们。
每当您在C++中使用new
时,都会通过调用sbrk
系统调用(或类似调用(本身的malloc
分配内存。因此,除了操作系统之外,没有人知道请求的大小。因此,您必须使用delete
(调用free
再次转到sbrk
(将内存返回给系统。否则,会出现内存泄漏。
现在,当涉及到第二种情况时,编译器已经知道分配的内存的大小。也就是说,在您的情况下,一int
的大小.设置指向此int
地址的指针不会更改所需内存的任何内容。或者换句话说:编译器能够处理释放内存的问题。在第一种情况下,new
这是不可能的。
除此之外:new
分别malloc
不需要精确分配所需的大小,这使得事情变得更加复杂。
编辑
两个更常见的短语:第一种情况也称为静态内存分配(由编译器完成(,第二种情况是指动态内存分配(由运行时系统完成(。
如果您的程序应该让用户存储任意数量的整数,会发生什么情况?然后,您需要在运行时根据用户的输入决定要分配多少整数,因此必须动态完成此操作。
简而言之,动态分配对象的生存期由您控制,而不是由语言控制。这允许您让它存活,只要它是必需的(而不是范围结束(,可能由只能在 run-rime 时计算的条件决定。
此外,动态内存通常更具"可扩展性",即与基于堆栈的分配相比,您可以分配更多和/或更大的对象。
分配本质上是"标记"一块内存,因此不能在同一空间中分配其他对象。取消分配"取消标记"该内存片段,以便它可以在以后的分配中重复使用。如果在不再需要内存后未能释放内存,则会得到一种称为"内存泄漏"的情况 - 程序占用不再需要的内存,导致可能无法分配新内存(由于缺少可用内存(,并且通常会给系统带来不必要的压力。
- 对具有动态分配的内存和析构函数的类对象的引用
- 调用析构函数以释放动态分配的内存
- 在运行时为动态分配的内存输入值
- 释放动态分配的内存时是否需要执行此额外步骤
- 动态分配字符数组的内存
- 销毁C++中动态分配的内存(数组对象)
- 删除类成员的动态分配内存的最佳方法是什么
- 动态分配的内存构造函数
- 为什么动态分配的内存总是16字节对齐
- 如果您为类的一个对象动态分配内存作为参数,会发生什么
- 为什么动态分配的两个变量的内存位置不是连续的?
- 为浮点数组动态分配内存
- 动态分配 8 字节的内存
- 包含动态分配内存作为值的映射的取消定位速度有多快?
- STD分配器是否会在堆上动态分配内存?它可以安全地删除内存吗?
- 为什么不能在 Visual C++ 中动态分配堆栈内存?但海湾合作委员会可以做到
- 在C 中动态分配的内存的问题
- 为什么在 C++ 执行删除操作后仍可以访问释放的动态分配的内存
- 对内存动态分配的类不使用"*"的逻辑
- 如果我将内存动态分配给静态变量,我应该释放它还是会自动释放它