C ++动态分配内存

时间:2011-12-14 11:57:35

标签: c++ memory-management

我不太了解动态分配的内存,我希望你们能让我更清楚。

首先,每次我们分配内存时,我们只需要一个指向该内存的指针。

int * dynInt = new int;

那么做我上面做的和做之间的区别是什么:

int someInt;
int* dynInt = &someInt;

据我所知,在这两种情况下,内存都是为int分配的,我们得到一个指向该内存的指针。

那么两者之间的区别是什么。何时一种方法优于另一种方法。

还有更多为什么我需要用

释放内存
delete dynInt;

在第一种情况下,但不是在第二种情况下。

我的猜测是:

  1. 当为一个对象动态分配内存时,该对象不会被初始化,而如果您执行类似于第二种情况的操作,则该对象将被初始化。如果这是唯一的区别,除了动态分配内存的速度更快之外,是否还有任何动机。

  2. 我们不需要对第二种情况使用delete的原因是因为对象被初始化的事实创建了某种自动销毁例程。

  3. 如果有人纠正我并为我澄清事情,那些只是猜测会喜欢它。

8 个答案:

答案 0 :(得分:15)

区别在于存储时间

  • 具有自动存储持续时间的对象是您的“普通”对象,它们会在定义它们的块的末尾自动超出范围。

    int someInt;

    一样创建它们

    你可能听说过它们是“堆栈对象”,虽然我对象这个术语。

  • 动态存储时间的对象具有“手动”生命周期;您必须使用delete自行销毁它们,并使用关键字new创建它们。

    你可能听说过它们是“堆对象”,但我也反对这一点。

指针的使用实际上与它们中的任何一个都没有严格的关系。您可以指向自动存储持续时间的对象(第二个示例),并且可以指向动态存储持续时间的对象(第一个示例)。

但是你很少想要一个指向自动对象的指针,因为:

  1. 你没有“默认”;
  2. 这个对象不会持续很长时间,所以你用这样的指针并不是很多。
  3. 相比之下,动态对象通常是通过指针访问的,因为语法接近强制执行它。 new返回一个指针供您使用,您必须将指针传递给delete,并且(除了使用引用之外)实际上没有其他方法可以访问该对象。它生活在一片充满活力的云中,而坐落在本地范围内。

    因此,指针的使用有时与动态存储的使用相混淆,但实际上前者与后者的因果关系不相关。

答案 1 :(得分:13)

像这样创建的对象:

int foo;

自动存储持续时间 - 对象一直存在,直到变量foo超出范围。这意味着在第一个示例中,dynInt一旦someInt超出范围(例如,在函数末尾),将成为无效指针。

像这样创建的对象:

int foo* = new int;

动态存储时间 - 对象一直存在,直到您明确调用delete为止。

对象的初始化是一个正交的概念;它与您使用的存储持续时间类型无直接关系。有关初始化的详细信息,请参阅here

答案 2 :(得分:4)

对于单个整数,只有在需要保留值后才有意义,例如,从函数返回。如果你按照你的说法宣布someInt,那么一旦它超出范围就会失效。

但是,通常情况下动态分配的用途更多。在分配之前,您的程序有许多事情不知道,并且取决于输入。例如,您的程序需要读取图像文件。那个图像文件有多大?我们可以说我们将它存储在这样的数组中:

unsigned char data[1000000];

但是,只有当图像大小小于或等于1000000字节时才会起作用,并且对于较小的图像也会浪费。相反,我们可以动态分配内存:

unsigned char* data = new unsigned char[file_size];

此处,file_size在运行时确定。在编译时你不可能告诉这个值。

答案 3 :(得分:4)

  1. 您的程序在启动时会获得初始内存块。此内存称为堆栈。这些天的金额通常约为2MB。

  2. 您的程序可以向操作系统询问额外的内存。这称为动态内存分配。这将在免费存储(C ++术语)或(C术语)上分配内存。您可以要求系统愿意提供尽可能多的内存(多个千兆字节)。

  3. 在堆栈上分配变量的语法如下所示:

    {
        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
    


    回答你的问题:

      

    何时一种方法优先于另一种方法。

    1. 通常首选堆栈分配。
    2. 当您需要使用其基类型存储多态对象时,需要动态分配。
    3. 始终使用智能指针自动删除:
      • C ++ 03:boost::scoped_ptrboost::shared_ptrstd::auto_ptr
      • C ++ 11:std::unique_ptrstd::shared_ptr
    4. 例如:

      // 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);
      
        

      此外,为什么我需要在第一种情况下释放内存,而不是在第二种情况下。

      正如我上面提到的,堆栈分配的变量会在范围退出时自动释放。 请注意,不允许删除堆栈内存。这样做会不可避免地导致您的应用程序崩溃。

答案 4 :(得分:2)

每当您在C ++中使用new时,内存都是通过malloc分配的,它会调用sbrk系统调用(或类似的)本身。因此,除OS之外,没有人知道所请求的大小。因此,您必须使用delete(再次调用free再次转到sbrk)将内存返回给系统。否则你会得到内存泄漏。

现在,当谈到第二种情况时,编译器会了解已分配内存的大小。也就是说,在您的情况下,大小为int。设置指向此int地址的指针不会改变所需内存的知识。或者换句话说:编译器能够处理释放内存的问题。在new的第一种情况下,这是不可能的。

除此之外:new分别malloc不需要精确分配请求的大小,这会使事情变得更复杂。

修改

两个更常见的短语:第一种情况也称为静态内存分配(由编译器完成),第二种情况是指动态内存分配(由运行时系统完成)。

答案 5 :(得分:2)

详细了解dynamic memory allocation以及garbage collection

你真的需要阅读一本好的C或C ++编程书

详细解释需要花费很多时间。

堆是在其中进行动态分配(在C ++中为new或在C中为malloc)的内存。有system calls涉及增长和缩小堆。在Linux上,它们是mmap & munmap(用于实现mallocnew等...)。

你可以多次调用分配原语。因此,您可以将int *p = new int;放入循环中,并在每次循环时获取一个新位置!

不要忘记释放内存(C ++中为delete或C中为free)。否则,你会得到一个memory leak - 一种顽皮的错误 - 。在Linux上,valgrind有助于捕获它们。

答案 6 :(得分:1)

如果您的程序应该允许用户存储任意数量的整数,会发生什么?然后,您需要在运行时根据用户的输入决定要分配多少个整数,因此必须动态完成。

答案 7 :(得分:1)

简而言之,动态分配对象的生命周期由您控制,而不是由语言控制。这允许您在需要时让它存活(而不是范围的结束),可能由只能在run-rime计算的条件决定。

此外,动态内存通常更具“可扩展性” - 即与基于堆栈的分配相比,您可以分配更多和/或更大的对象。

分配基本上“标记”了一块内存,因此不能在同一空间中分配其他对象。取消分配“取消标记”该内存,以便可以将其重新用于以后的分配。如果在不再需要内存后无法释放内存,则会出现称为“内存泄漏”的情况 - 您的程序占用了不再需要的内存,导致可能无法分配新内存(由于缺少免费内存)记忆),通常只是给系统带来不必要的压力。