5

我正在学习 C++ 中的动态内存分配以及关键字newnew[]被提及。据说可以让用户在运行时指定内存分配的大小,而不是简单地在源代码中声明一个具有固定大小的变量或数组。

我不明白这个概念。它是如何工作的?我只需要澄清这个想法,一个例子会很有帮助!

4

2 回答 2

10

所以,如果你想要一个 10 个整数的数组,你会写:

int arr[10]; 

但是如果你想做这样的事情怎么办?

cout << "How many?";
cin >> num;

int arr[num];

好吧,C++ 语言不允许这样做。相反,您必须这样做:

int *arr = new int[num]; 

创建你的数组。稍后您必须[1] 使用:

delete [] arr; 

释放内存。

那么,这是如何工作的呢?当您调用 new 时,C++ 运行时库 [构成 C++ 基础的您不必编写的代码] 将计算出num整数占用了多少空间,并为此在内存中找到一些空间。我不会详细介绍“你如何找到一些记忆”。现在,相信我,有一些可用的内存可以用来存储一些整数。

当您稍后调用delete时,相同的内存将返回给它来自的内存“池”或“堆”。

当然,如果您有一台内存为 256 MB 的机器,并且您尝试请求空间来存储 2.5 亿个整数,请记住一个整数占用超过一个字节,这不会成功 -这里没有“魔法” - 内存仍然限于机器中可用的内存量......您有权在程序中确定程序何时运行,您需要多少内存,而不是必须决定何时编写程序。

编辑:通常最好使用已经存在的“容器”和“包装器类”来“隐藏”任何内存分配,这对这个目的非常有用。例如:

 std::vector<int> arr;

将用作整数的变量存储,您不必担心释放内存,甚至在将它们存储在那里之前就知道需要多少。

 std::shared_ptr<int> arr = new int[num]; 

是另一种情况,当“shared_ptr”不再使用时[它在共享指针类中跟踪它,所以你永远不需要关心释放内存]。

[1] 如果您不想泄漏内存,那么泄漏内存是“不好的风格”。如果你这样做,不会让任何人开心。

于 2013-01-29T02:25:25.797 回答
5

我看过很多关于 C++ 中内存分配的帖子,关于“new operator”与“operator new”的问题,关于new int(100)vsnew int[100]的问题,关于内存初始化的问题......我认为应该有一个答案可以一劳永逸地总结一切,我选择这个问题来写这个总结。它与动态内存分配有关,运行时在堆上的分配。我还提供了一个摘要实现(公共领域)。


C 与 C++

动态内存分配的主要功能:

  • 在 C (header <cstdlib>) 中,我们主要有mallocandcallocfree。我就不说了realloc
  • 在 C++ (header <new>) 中,我们有:
    • 带有初始化参数的模板单对象分配:
      • new T( args )
      • new (std::nothrow) T( args )
      • delete ( T* )
    • 具有默认初始化的模板多对象分配:
      • new T[ size_t ]
      • new (std::nothrow) T[ size_t ]
      • delete[] ( T* )
    • 模板内存初始化,无需为单个或多个对象分配:
      • new (void*) T( args )
      • new (void*) T[ size_t ]
    • 内部新表达式
      • 原始内存分配::operator new( size_t )
      • 原始内存分配无一例外::operator new( size_t, std::nothrow )
      • 未分配的原始内存初始化::operator new( size_t, ptr )

请查看这篇文章以获得简洁的比较。


旧版 C 动态分配

要点:完整的类型擦除(void*指针),因此没有构造/破坏,以字节为单位指定的大小(通常使用sizeof)。

malloc( size_t )根本不初始化内存(原始内存包含垃圾,总是在使用前手动初始化)。calloc( size_t, size_t )将所有位初始化为 0(开销很小,但对 POD 数字类型很有用)。任何分配的内存都应该使用ONLY释放。free

类实例的构造/销毁 使用/内存释放前手动完成。


C++ 动态分配

要点:由于相似的语法做不同的事情而令人困惑,所有 delete-statements 调用析构函数,所有 delete-statements 采用全类型指针,一些 new-statements 返回全类型指针,一些 new-statements 调用一些构造函数。

警告:正如您将在下面看到的,new可以是关键字函数。最好不要谈论“new operator”和/或“operator new”以避免混淆。我将new任何包含new函数或关键字的有效语句称为“-statements”。人们还谈论“new-expressions”,其中new是关键字而不是函数。

原始内存分配(无初始化)

不要自己使用它。这由new-expressions内部使用(见下文)。

这些分配不会初始化内存,特别是它们不会调用分配对象的默认构造函数。因此,您必须在使用或释放分配之前手动初始化所​​有元素deletedelete[]

注意:我不能强调你不应该自己使用它。但是,如果您应该使用它,请确保或void时传递一个指针而不是类型化指针(始终在手动初始化之后)。我亲身经历过使用某些编译器的非 POD 类型的运行时错误(也许是我的错误)。deletedelete[]

原始内存初始化(无分配)

不要自己使用它。这由new-expressions内部使用(见下文)。在下文中,我假设void *ptr = ::operator new( n*sizeof(T) )一些 typeT和 size n

然后从使用默认构造函数开始::operator new( n*sizeof(T), (T*) ptr )初始化n类型的元素。这里没有分配,只使用默认构造函数进行初始化。TptrT::T()

单对象分配和初始化

  • new T( args )使用构造函数为单个类型的对象分配初始化内存。除非省略参数(即,甚至) ,否则不会调用默认构造函数。失败时抛出异常。TT::T( args )new T()new Tstd::bad_alloc
  • 相同的new (std::nothrow) T( args )只是它NULL在失败的情况下返回。
  • 用于delete调用析构函数T::~T()并释放相应的内存。

多对象分配和初始化

  • new T[n]使用默认构造函数为类型对象分配初始化内存。失败时抛出异常。nTstd::bad_alloc
  • 同上,new (std::nothrow) T[n]除了它NULL在失败的情况下返回。
  • 用于为每个元素delete[]调用析构函数并释放相应的内存。T::~T()

内存初始化(又名“放置新”)

这里没有分配。不管如何分配:

  • new (ptr) T(args)调用T::T(args)存储在的内存上的构造函数ptr。除非省略参数,否则不会调用默认构造函数。
  • new (ptr) T[n]对存储 from to类型T::T()的对象(即字节)调用默认构造函数。nTptrptr+nn*sizeof(T)

相关文章

于 2014-12-24T01:03:12.100 回答