[英]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
。
对象的初始化是一个正交的概念; 它与您使用的存储持续时间类型无直接关系。 有关初始化的更多信息,请参见此处
对于单个整数,只有在需要保留值后才有意义,例如,从函数返回。 如果你按照你的说法声明了someInt
,那么一旦它超出范围就会失效。
但是,通常动态分配有更多用途。 在分配之前,您的程序有许多事情不知道,并且取决于输入。 例如,您的程序需要读取图像文件。 那个图像文件有多大? 我们可以说我们将它存储在这样的数组中:
unsigned char data[1000000];
但这仅在图像大小小于或等于1000000字节时才有效,并且对于较小的图像也会浪费。 相反,我们可以动态分配内存:
unsigned char* data = new unsigned char[file_size];
这里, file_size
是在运行时确定的。 在编译时你不可能告诉这个值。
您的程序在启动时获得初始内存块。 这个内存称为堆栈 。 这些天的金额通常约为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
回答你的问题:
何时一种方法优于另一种方法。
boost::scoped_ptr
, boost::shared_ptr
或std::auto_ptr
。 std::unique_ptr
或std::shared_ptr
。 例如:
// 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);
还有更多为什么我需要在第一种情况下释放内存,而不是在第二种情况下。
正如我上面提到的,堆栈分配的变量会在范围退出时自动释放。 请注意,不允许删除堆栈内存。 这样做会不可避免地导致您的应用程序崩溃。
每当你在C ++中使用new
,内存都是通过malloc
分配的,它调用sbrk
系统调用(或类似的)本身。 因此,除OS之外,没有人知道所请求的大小。 所以你必须使用delete
(再次调用sbrk
free
)来将内存返回给系统。 否则你会得到内存泄漏。
现在,当谈到第二种情况时,编译器会了解分配的内存大小。 也就是说,在你的情况下,一个int
的大小。 设置指向此int
地址的指针不会改变所需内存的知识。 或者换句话说:编译器能够处理释放内存的问题。 在new
的第一种情况下,这是不可能的。
除此之外: new
malloc
不需要精确分配所需的大小,这使得事情变得复杂一些。
编辑
两个更常见的短语:第一种情况也称为静态内存分配(由编译器完成),第二种情况是指动态内存分配(由运行时系统完成)。
你真的需要阅读一本好的C或C ++编程书 。
详细解释需要花费很多时间。
堆是在其中发生动态分配(在C ++中使用new
或在C中使用malloc
)的malloc
。 有关系统调用涉及增长和缩小堆。 在Linux上,它们是mmap和munmap (用于实现malloc
和new
等...)。
你可以调用很多次分配原语。 所以你可以把int *p = new int;
在循环内部,每次循环时都获得一个新的位置!
不要忘记释放内存(在C ++中delete
或在C中free
)。 否则,你会得到一个内存泄漏 - 一种顽皮的错误 - 。 在Linux上, valgrind有助于捕获它们。
如果您的程序应该让用户存储任意数量的整数会发生什么? 然后,您需要在运行时根据用户的输入决定要分配多少个整数,因此必须动态完成。
简而言之,动态分配对象的生命周期由您控制,而不是由语言控制。 这允许您在需要时让它存活(而不是范围的结束),可能由只能在run-rime计算的条件决定。
此外,动态内存通常更具“可伸缩性” - 即与基于堆栈的分配相比,您可以分配更多和/或更大的对象。
分配基本上“标记”一块内存,因此不能在同一空间中分配其他对象。 取消分配“取消标记”该内存,以便可以将其重新用于以后的分配。 如果在不再需要内存后无法释放内存,则会出现称为“内存泄漏”的情况 - 您的程序占用了不再需要的内存,导致可能无法分配新内存(由于缺少免费内存)记忆),通常只是给系统带来不必要的压力。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.