我正在学习 C++ 中的动态内存分配以及关键字new
和new[]
被提及。据说可以让用户在运行时指定内存分配的大小,而不是简单地在源代码中声明一个具有固定大小的变量或数组。
我不明白这个概念。它是如何工作的?我只需要澄清这个想法,一个例子会很有帮助!
我正在学习 C++ 中的动态内存分配以及关键字new
和new[]
被提及。据说可以让用户在运行时指定内存分配的大小,而不是简单地在源代码中声明一个具有固定大小的变量或数组。
我不明白这个概念。它是如何工作的?我只需要澄清这个想法,一个例子会很有帮助!
所以,如果你想要一个 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)
vsnew int[100]
的问题,关于内存初始化的问题......我认为应该有一个答案可以一劳永逸地总结一切,我选择这个问题来写这个总结。它与动态内存分配有关,即运行时在堆上的分配。我还提供了一个摘要实现(公共领域)。
动态内存分配的主要功能:
<cstdlib>
) 中,我们主要有malloc
andcalloc
和free
。我就不说了realloc
。<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 )
。请查看这篇文章以获得简洁的比较。
要点:完整的类型擦除(void*
指针),因此没有构造/破坏,以字节为单位指定的大小(通常使用sizeof
)。
malloc( size_t )
根本不初始化内存(原始内存包含垃圾,总是在使用前手动初始化)。calloc( size_t, size_t )
将所有位初始化为 0(开销很小,但对 POD 数字类型很有用)。任何分配的内存都应该使用ONLY释放。free
类实例的构造/销毁应 在使用前/内存释放前手动完成。
要点:由于相似的语法做不同的事情而令人困惑,所有 delete
-statements 调用析构函数,所有 delete
-statements 采用全类型指针,一些 new
-statements 返回全类型指针,一些 new
-statements 调用一些构造函数。
警告:正如您将在下面看到的,new
可以是关键字或函数。最好不要谈论“new operator”和/或“operator new”以避免混淆。我将new
任何包含new
函数或关键字的有效语句称为“-statements”。人们还谈论“new
-expressions”,其中new
是关键字而不是函数。
不要自己使用它。这由new-expressions内部使用(见下文)。
::operator new( size_t )
并::operator new( size_t, std::nothrow )
以字节为单位获取大小,并void*
在成功的情况下返回 a。std::bad_alloc
,后者返回NULL
。::operator new( sizeof(T) )
的单个对象T
(和用于delete
发布)和多个对象(和用于发布)。::operator new( n*sizeof(T) )
delete[]
这些分配不会初始化内存,特别是它们不会调用分配对象的默认构造函数。因此,您必须在使用或释放分配之前手动初始化所有元素。delete
delete[]
注意:我不能强调你不应该自己使用它。但是,如果您应该使用它,请确保或void
时传递一个指针而不是类型化指针(始终在手动初始化之后)。我亲身经历过使用某些编译器的非 POD 类型的运行时错误(也许是我的错误)。delete
delete[]
不要自己使用它。这由new-expressions内部使用(见下文)。在下文中,我假设void *ptr = ::operator new( n*sizeof(T) )
一些 typeT
和 size n
。
然后从使用默认构造函数开始::operator new( n*sizeof(T), (T*) ptr )
初始化n
类型的元素。这里没有分配,只使用默认构造函数进行初始化。T
ptr
T::T()
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
。除非省略参数,否则不会调用默认构造函数。new (ptr) T[n]
对存储 from to类型T::T()
的对象(即字节)调用默认构造函数。n
T
ptr
ptr+n
n*sizeof(T)