我正在学习C ++中的动态内存分配,并提到了关键字new
和new[]
。
据说,用户可以在运行时指定内存分配的大小,而不像在源代码中声明一个具有固定大小的变量或数组。
我不明白这个概念。它是如何工作的?我只需要澄清这个想法,一个例子会有所帮助!
答案 0 :(得分:9)
所以,如果你想要一个包含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]如果你不想泄漏内存,那么泄漏内存就是“坏风格”。如果你这样做,不会让任何人高兴。
答案 1 :(得分:4)
我在C ++中看过很多关于内存分配的帖子,关于&#34;新运算符&#34; vs&#34; operator new&#34;,关于new int(100)
vs new int[100]
的问题,关于内存初始化的问题......我认为应该有一个答案可以一劳永逸地总结一切,我和#39;我选择这个问题来撰写本摘要。它是关于动态内存分配,即在运行时堆上的分配。我还提供summary implementation(公共领域)。
动态内存分配的主要功能:
<cstdlib>
)中,我们主要有malloc
和calloc
以及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 )
的原始内存初始化。请查看this post进行简明比较。
要点 :完成类型擦除(void*
指针),因此无构造/销毁,指定的大小字节(通常使用sizeof
)。
malloc( size_t )
根本没有初始化内存(原始内存包含垃圾,在使用前总是手动初始化)。 calloc( size_t, size_t )
将所有位初始化为0(略微开销,但对POD数字类型有用)。应使用free
仅释放任何已分配的内存。
在内存释放之前使用/ 之前,的构建/销毁应该手动完成 。
要点 :由于类似语法执行不同的操作而导致混淆,所有 delete
- 语句调用析构函数,所有 delete
- 语句采用完全类型的指针,一些 new
- 语句返回完全类型的指针,一些 {{1 - 语句调用一些构造函数。
警告 :正如您将在下面看到的那样,new
可以是关键字或功能。最好不要谈论&#34;新的运营商&#34;和/或&#34;运营商新&#34;为了avoid confusions。我打电话给#34; new
- 陈述&#34;包含new
作为函数或关键字的任何有效语句。人们还谈论&#34; new
- 表达式&#34;,其中new
是关键字而不是函数。
不要自己使用。这是由 new-expressions 在内部使用的(见下文)。
new
和::operator new( size_t )
取一个字节大小,如果成功则返回::operator new( size_t, std::nothrow )
。std::bad_alloc
,后者返回void*
。::operator new( sizeof(T) )
用于NULL
类型的单个对象(delete
用于发布),::operator new( n*sizeof(T) )
用于倍数< / strong>对象(和delete[]
发布)。这些分配不初始化内存,特别是不调用已分配对象的默认构造函数。因此,在使用T
或delete
发布分配之前,必须手动初始化所有元素。
注意 :我没有足够的压力,你不应该自己使用它。但是,如果您应该使用它,请确保在此类分配上调用delete[]
或void
时(通常在手动初始化之后),将指针传递给delete
而不是类型指针。我亲身经历过非POD类型的运行时错误和一些编译器(可能是我的错误)。
不要自己使用。这是由 new-expressions 在内部使用的(见下文)。
在下文中,我假设某些类型delete[]
和大小void *ptr = ::operator new( n*sizeof(T) )
为T
。
然后n
使用默认构造函数::operator new( n*sizeof(T), (T*) ptr )
从n
开始初始化T
类型ptr
元素。这里有 no allocation ,只使用default-constructor进行初始化。
T::T()
使用构造函数new T( args )
为<{1}}类型的单个对象分配和初始化内存。除非省略参数(即T
或甚至T::T( args )
),否则不会将默认构造函数称为。失败时引发异常new T()
。new T
相同,只是在失败时返回std::bad_alloc
。new (std::nothrow) T( args )
调用析构函数NULL
并释放相应的内存。delete
使用默认构造函数为<{1}}类型的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]
类T::T()
对象的默认构造函数n
(即{{1} }} bytes。。