什么是C ++中的动态内存分配?

我正在学习C ++中的动态内存分配,并提到了newnew[]关键字。 据说使用户能够在运行时指定内存分配的大小,而不像在源代码中只声明一个固定大小的变量或数组。

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


所以,如果你想要一个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]如果你不想泄漏内存,并且泄漏内存是“坏风格”。 如果你这样做不会让任何人开心。


我见过很多关于C ++内存分配的帖子,关于“new operator”和“operator new”的问题,关于new int(100)new int[100]问题,关于内存初始化的问题......我认为应该有一个清楚地总结所有事情的答案,我选择这个问题来写这个总结。 它是关于动态内存分配的,即在运行时在堆上进行分配。 我还提供了一个总结实施(公共领域)。


C vs C ++

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

  • 在C(header <cstdlib> )中,我们主要有malloccalloc并且是free 。 我不会谈论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数字类型有用)。 任何分配的内存应该用发行free ONLY。

    构造/销毁类实例应该在使用之前/释放内存之前手动完成


    C ++动态分配

    要点 :因类似的语法做不同的事情而引起混淆, 所有的 delete语句调用析构函数, 所有的 delete语句带有完全类型的指针, 一些 new语句返回完全类型的指针, 一些 new语句调用一些构造函数。

    警告 :正如您将在下面看到的, new可以是关键字OR函数。 最好不要谈论“新运营商”和/或“新运营商”,以避免混淆。 我将任何包含new有效语句作为函数或关键字调用“ new -statements”。 人们还谈论“ new ”, new关键词而不是功能。

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

    不要自己使用这个。 这由新表达式在内部使用(见下文)。

  • ::operator new( size_t )::operator new( size_t, std::nothrow )以字节为单位获取大小,并在成功时返回void*
  • 在失败的情况下,前者抛出一个异常std::bad_alloc ,后者返回NULL
  • 使用::operator new( sizeof(T) )T类型的单个对象(和delete以释放),以及::operator new( n*sizeof(T) )用于多个对象(和delete[]以释放)。
  • 这些分配不会初始化内存,特别是它们不会在分配的对象上调用默认构造函数。 因此,在使用deletedelete[]释放分配之前,您必须手动初始化所​​有元素

    注意 :我无法强调你不应该自己使用它。 但是,如果您应该使用它,请确保在调用此类分配中的deletedelete[] (通常在手动初始化后)时,将指针传递给void而不是类型指针。 我有一些编译器遇到了非POD类型的运行时错误(也许是我的错误)。

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

    不要自己使用这个。 这由新表达式在内部使用(见下文)。 在下面,我假设对于某些类型T和大小为n void *ptr = ::operator new( n*sizeof(T) )

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

    单对象分配和初始化

  • new T( args )分配和用于类型的单个对象初始化存储器T使用构造T::T( args ) 。 默认的构造函数不会被调用,除非参数被省略(即new T()或甚至new T )。 失败时抛出一个异常std::bad_alloc
  • new (std::nothrow) T( args )除外,如果失败则返回NULL
  • 使用delete调用析构函数T::~T()并释放相应的内存。
  • 多对象分配和初始化

  • new T[n]分配和用于初始化存储器n类型的对象T使用默认构造。 失败时抛出一个异常std::bad_alloc
  • 同样是new (std::nothrow) T[n]只是它在失败的情况下返回NULL
  • 使用delete[]为每个元素调用析构函数T::~T()并释放相应的内存。
  • 内存初始化(又名“放置新的”)

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

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

  • 相关文章

  • 简洁比较new / delete vs malloc / free
  • 更详细的Malloc与新的,看看@Flexo的答案
  • 新的运营商vs运营商新,避免不使用这些条款的混淆
  • 链接地址: http://www.djcxy.com/p/2213.html

    上一篇: What is Dynamic Memory Allocation in C++?

    下一篇: In what cases do I use malloc vs new?