447 c++ malloc memory-management new-operator
我在C++中看到有多种方式来分配和释放数据,我知道当你打电话给malloc
你时应该打电话free
,当你使用new
操作员时你应该配对,delete
将两者混合是错误的(例如,调用free()
创建的东西)与new
操作员),但我不知道何时应该使用malloc
/ free
何时应该在我的真实世界程序中使用new
/ delete
.
如果您是C++专家,请告诉我您在此方面遵循的任何经验法则或惯例.
Bri*_*ndy 360
除非你被迫使用C,否则你永远不 应该使用malloc
.一直用new
.
如果您需要大量数据,请执行以下操作:
char *pBuffer = new char[1024];
Run Code Online (Sandbox Code Playgroud)
虽然这不正确但要小心:
//This is incorrect - may delete only one element, may corrupt the heap, or worse...
delete pBuffer;
Run Code Online (Sandbox Code Playgroud)
相反,您应该在删除数据数组时执行此操作:
//This deletes all items in the array
delete[] pBuffer;
Run Code Online (Sandbox Code Playgroud)
该new
关键字是做它的C++的方式,这将确保你的类型都会有所谓的构造.该new
关键字也更加类型安全,而malloc
不是类型安全的.
malloc
如果您需要更改数据缓冲区的大小,我认为唯一有益于使用的方法就是使用它.该new
关键字没有像类似的方式realloc
.该realloc
函数可能能够更有效地扩展一块内存的大小.
值得一提的是你不能混用new
/ free
和malloc
/ delete
.
注意:此问题中的某些答案无效.
int* p_scalar = new int(5); // Does not create 5 elements, but initializes to 5
int* p_array = new int[5]; // Creates 5 elements
Run Code Online (Sandbox Code Playgroud)
Fle*_*exo 140
简短的回答是:如果没有malloc
充分的理由,请不要使用C++.malloc
与C++一起使用时有许多不足之处,C++ new
被定义为可以克服.
malloc
以任何有意义的方式都不是类型安全的.在C++中,您需要转换来自void*
.这可能会引入很多问题:
#include <stdlib.h>
struct foo {
double d[5];
};
int main() {
foo *f1 = malloc(1); // error, no cast
foo *f2 = static_cast<foo*>(malloc(sizeof(foo)));
foo *f3 = static_cast<foo*>(malloc(1)); // No error, bad
}
Run Code Online (Sandbox Code Playgroud)但它比这更糟糕.如果有问题的类型是POD(普通旧数据),那么你可以半合理地使用它malloc
来为它分配内存,就像f2
第一个例子中那样.
如果一个类型是POD,那就不那么明显了.事实上,给定类型可能从POD更改为非POD而没有产生编译器错误并且可能非常难以调试问题是一个重要因素.例如,如果有人(可能是另一个程序员,在维护期间,很久以后要做出导致foo
不再是POD 的更改,那么在编译时不会出现明显的错误,例如:
struct foo {
double d[5];
virtual ~foo() { }
};
Run Code Online (Sandbox Code Playgroud)
将使malloc
中f2
也变得不好,没有任何明显的诊断.这里的示例是微不足道的,但是可能会意外地将非PODness引入更远的地方(例如,在基类中,通过添加非POD成员).如果您有C++ 11/boost,您可以使用它is_pod
来检查这个假设是否正确,如果不是,则会产生错误:
#include <type_traits>
#include <stdlib.h>
foo *safe_foo_malloc() {
static_assert(std::is_pod<foo>::value, "foo must be POD");
return static_cast<foo*>(malloc(sizeof(foo)));
}
Run Code Online (Sandbox Code Playgroud)
虽然boost 无法确定某个类型是否为没有C++ 11或其他编译器扩展的POD.
malloc
NULL
如果分配失败则返回.new
会扔std::bad_alloc
.稍后使用NULL
指针的行为是未定义的.抛出异常时会出现干净的语义,并且会从错误源中抛出异常.malloc
在每次通话时使用适当的测试进行包装似乎很乏味且容易出错.(你只需要忘记一次撤消所有好的工作).可以允许异常传播到调用者能够合理地处理它的级别,其中NULL
更有意义地传递回来.我们可以扩展我们的safe_foo_malloc
函数来抛出异常或退出程序或调用一些处理程序:
#include <type_traits>
#include <stdlib.h>
void my_malloc_failed_handler();
foo *safe_foo_malloc() {
static_assert(std::is_pod<foo>::value, "foo must be POD");
foo *mem = static_cast<foo*>(malloc(sizeof(foo)));
if (!mem) {
my_malloc_failed_handler();
// or throw ...
}
return mem;
}
Run Code Online (Sandbox Code Playgroud)从根本上说malloc
是C功能并且new
是C++功能.结果malloc
不能很好地与构造函数一起使用,它只关注分配一块字节.我们可以safe_foo_malloc
进一步扩展我们的使用位置new
:
#include <stdlib.h>
#include <new>
void my_malloc_failed_handler();
foo *safe_foo_malloc() {
void *mem = malloc(sizeof(foo));
if (!mem) {
my_malloc_failed_handler();
// or throw ...
}
return new (mem)foo();
}
Run Code Online (Sandbox Code Playgroud)我们的safe_foo_malloc
功能不是很通用 - 理想情况下我们想要的东西可以处理任何类型,而不仅仅是foo
.我们可以使用非默认构造函数的模板和可变参数模板实现此目的:
#include <functional>
#include <new>
#include <stdlib.h>
void my_malloc_failed_handler();
template <typename T>
struct alloc {
template <typename ...Args>
static T *safe_malloc(Args&&... args) {
void *mem = malloc(sizeof(T));
if (!mem) {
my_malloc_failed_handler();
// or throw ...
}
return new (mem)T(std::forward(args)...);
}
};
Run Code Online (Sandbox Code Playgroud)
现在虽然在解决我们到目前为止确定的所有问题时,我们实际上已经彻底改造了默认new
运营商.如果您打算使用malloc
和放置,new
那么您也new
可以开始使用!
Mat*_*ard 51
来自C++ FQA Lite:
[16.4]为什么我应该使用new而不是值得信赖的旧malloc()?
FAQ:new/delete调用构造函数/析构函数; new是类型安全的,malloc不是; new可以被类覆盖.
FQA:FAQ提到的新的优点不是美德,因为构造函数,析构函数和运算符重载都是垃圾(看看没有垃圾收集会发生什么?),类型安全问题在这里很小(通常你有)将malloc返回的void*转换为右指针类型,将其分配给一个类型化的指针变量,这可能很烦人,但远非"不安全").
哦,使用值得信赖的旧malloc可以使用同样值得信赖的旧realloc.太糟糕了,我们没有一个闪亮的新操作员更新或什么.
尽管如此,即使语言是C++,新的也不足以证明偏离整个语言使用的共同风格是正确的.特别是,如果只是对象进行malloc操作,那么具有非平凡构造函数的类将会以致命的方式行为异常.那么为什么不在整个代码中使用新的呢?人们很少会将操作员重新设置为新的,所以它可能不会过多地妨碍你.如果他们确实过载新的,你总是可以让他们停下来.
对不起,我无法抗拒.:)
Fer*_*cio 49
始终在C++中使用new.如果需要一个无类型内存块,可以直接使用operator new:
void *p = operator new(size);
...
operator delete(p);
Run Code Online (Sandbox Code Playgroud)
dmc*_*kee 31
使用malloc
并只用于分配要由C-中心库和API来管理内存.对您控制的所有内容使用和(以及变体).free
new
delete
[]
Yog*_*H T 26
new vs malloc()
1)new
是一个操作员,同时malloc()
也是一个功能.
2)new
调用构造函数,而不调用malloc()
.
3)new
返回确切的数据类型,同时malloc()
返回void*.
4)new
将不会返回NULL(将抛出失败),而malloc()
返回NULL
5)的存储器再分配不通过处理new
而malloc()
可以
The*_*ist 16
要回答你的问题,你应该知道的区别malloc
和new
.区别很简单:
malloc
分配内存,同时new
分配内存并调用你正在为其分配内存的对象的构造函数.
因此,除非您被限制为C,否则不应使用malloc,尤其是在处理C++对象时.这将是破坏你的程序的一个秘诀.
此外之间的差别free
,并delete
是完全一样的.区别在于delete
除了释放内存之外还将调用对象的析构函数.
R. *_*des 11
malloc
和之间有一个很大的区别new
.malloc
分配内存.这对于C来说很好,因为在C中,一块内存是一个对象.
在C++中,如果您不处理POD类型(类似于C类型),则必须在内存位置调用构造函数以实际拥有对象.非POD类型在C++中非常常见,因为许多C++特性使对象自动成为非POD.
new
分配内存并在该内存位置创建一个对象.对于非POD类型,这意味着调用构造函数.
如果您这样做:
non_pod_type* p = (non_pod_type*) malloc(sizeof *p);
Run Code Online (Sandbox Code Playgroud)
您获取的指针无法解除引用,因为它不指向对象.在使用之前,您需要在其上调用构造函数(这是使用展示位置完成的new
).
另一方面,如果你这样做:
non_pod_type* p = new non_pod_type();
Run Code Online (Sandbox Code Playgroud)
你得到一个始终有效的指针,因为new
创建了一个对象.
即使对于POD类型,两者之间也存在显着差异:
pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;
Run Code Online (Sandbox Code Playgroud)
这段代码会打印一个未指定的值,因为创建的POD对象malloc
未初始化.
使用new
,您可以指定要调用的构造函数,从而获得明确定义的值.
pod_type* p = new pod_type();
std::cout << p->foo; // prints 0
Run Code Online (Sandbox Code Playgroud)
如果你真的想要它,你可以使用use new
来获得未初始化的POD对象.有关详细信息,请参阅此其他答案.
另一个区别是失败时的行为.当它无法分配内存时,malloc
返回空指针,同时new
抛出异常.
前者要求您在使用之前测试返回的每个指针,而后者将始终生成有效的指针.
出于这些原因,您应该使用C++代码new
,而不是malloc
.但即便如此,您也不应该使用new
"公开",因为它会获取您稍后需要释放的资源.使用时new
,应将其结果立即传递到资源管理类:
std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
Run Code Online (Sandbox Code Playgroud)
JVA*_*pen 10
仅当对象的生命周期与其创建的作用域不同时才需要动态分配(这对于使作用域变大变小也成立)并且您有一个特定的原因不按值存储它工作。
例如:
std::vector<int> *createVector(); // Bad
std::vector<int> createVector(); // Good
auto v = new std::vector<int>(); // Bad
auto result = calculate(/*optional output = */ v);
auto v = std::vector<int>(); // Good
auto result = calculate(/*optional output = */ &v);
Run Code Online (Sandbox Code Playgroud)
从 C++11 开始,我们必须std::unique_ptr
处理分配的内存,其中包含已分配内存的所有权。std::shared_ptr
是为您必须共享所有权而创建的。(在一个好的程序中,你需要的比你期望的要少)
创建实例变得非常简单:
auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::unique_ptr<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::unique_ptr<Class[]>(new Class[](42)); // C++11
Run Code Online (Sandbox Code Playgroud)
C++17 还添加了std::optional
可以防止您需要内存分配的功能
auto optInstance = std::optional<Class>{};
if (condition)
optInstance = Class{};
Run Code Online (Sandbox Code Playgroud)
一旦“实例”超出范围,内存就会被清理。转让所有权也很容易:
auto vector = std::vector<std::unique_ptr<Interface>>{};
auto instance = std::make_unique<Class>();
vector.push_back(std::move(instance)); // std::move -> transfer (most of the time)
Run Code Online (Sandbox Code Playgroud)
那你new
什么时候还需要?从 C++11 开始几乎没有。您使用的大部分内容,std::make_unique
直到您到达通过原始指针传输所有权的 API 为止。
auto instance = std::make_unique<Class>();
legacyFunction(instance.release()); // Ownership being transferred
auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr
Run Code Online (Sandbox Code Playgroud)
在 C++98/03 中,您必须进行手动内存管理。如果您遇到这种情况,请尝试升级到更新版本的标准。如果你卡住了:
auto instance = new Class(); // Allocate memory
delete instance; // Deallocate
auto instances = new Class[42](); // Allocate memory
delete[] instances; // Deallocate
Run Code Online (Sandbox Code Playgroud)
确保您正确跟踪所有权,以免出现任何内存泄漏!移动语义也不起作用。
那么,我们什么时候需要在 C++ 中使用 malloc 呢?唯一有效的原因是分配内存并稍后通过placement new 对其进行初始化。
auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
auto instance = new(instanceBlob)Class{}; // Initialize via constructor
instance.~Class(); // Destroy via destructor
std::free(instanceBlob); // Deallocate the memory
Run Code Online (Sandbox Code Playgroud)
尽管上述内容是有效的,但这也可以通过 new-operator 来完成。std::vector
是一个很好的例子。
最后,我们房间里还有大象:C
。如果您必须使用 C 库,其中内存在 C++ 代码中分配并在 C 代码中释放(或其他方式),您将被迫使用 malloc/free。
如果你在这种情况下,忘记虚函数、成员函数、类......只允许包含 POD 的结构。
规则的一些例外情况:
有一些事情new
没有malloc
做到:
new
通过调用该对象的构造函数来构造对象new
不需要对已分配的内存进行类型转换.所以,如果你使用malloc
,那么你需要明确地做上面的事情,这并不总是实际的.另外,new
可以重载但malloc
不能.
小智 6
如果您使用 C++,请尝试使用 new/delete 而不是 malloc/calloc,因为它们是运算符。对于 malloc/calloc,您需要包含另一个标头。不要在同一代码中混合两种不同的语言。他们的工作在各个方面都很相似,都是从哈希表的堆段动态分配内存。
如果您使用不需要构造/销毁并且需要重新分配的数据(例如,大量的int),那么我相信malloc / free是一个不错的选择,因为它可以为您提供重新分配,这比new-memcpy更快-delete(在我的Linux机器上,但是我想这可能取决于平台)。如果使用不是POD且需要构造/销毁的C ++对象,则必须使用new和delete运算符。
无论如何,我不明白为什么不应该同时使用两种方法(前提是您释放了已分配的内存并删除了用new分配的对象),如果可以利用速度提升的优势(如果您要重新分配大型数组,有时会很重要)重新分配可以给您的POD)。
除非您需要,否则应坚持使用C ++中的new / delete。