我看到在C ++中有多种分配和释放数据的方法,并且我了解,当您调用malloc
您应该调用free
而当您使用new
运算符时,您应该与delete
配对,并且将两者混用是错误的(例如,调用free()
在使用new
运算符创建的内容上),但是我不清楚在实际程序中何时应使用malloc
/ free
以及何时应使用new
/ delete
。
如果您是C ++专家,请让我知道您在这方面遵循的经验法则或惯例。
#1楼
malloc()用于动态分配C中的内存,而c ++中的new()完成相同的工作。 因此,您不能混合使用两种语言的编码约定。 如果您要求calloc和malloc()之间的区别会很好
#2楼
在以下情况下,我们不能使用new,因为它调用了构造函数。
class B {
private:
B *ptr;
int x;
public:
B(int n) {
cout<<"B: ctr"<<endl;
//ptr = new B; //keep calling ctr, result is segmentation fault
ptr = (B *)malloc(sizeof(B));
x = n;
ptr->x = n + 10;
}
~B() {
//delete ptr;
free(ptr);
cout<<"B: dtr"<<endl;
}
};
#3楼
如果您使用不需要构造/销毁并且需要重新分配的数据(例如,大量的int),那么我相信malloc / free是一个不错的选择,因为它可以为您提供重新分配,这比new-memcpy更快-delete(在我的Linux机器上,但是我想这可能取决于平台)。 如果使用不是POD且需要构造/销毁的C ++对象,则必须使用new和delete运算符。
无论如何,我不明白为什么不应该同时使用两种方法(前提是您释放了已分配的内存并删除了用new分配的对象),如果可以利用速度提升的优势(如果您要重新分配大型数组,有时会很重要)重新分配可以给您的POD)。
除非您需要,否则应坚持使用C ++中的new / delete。
#4楼
除非您被迫使用C,否则永远不要使用 malloc
。 始终使用new
。
如果您需要大量数据,请执行以下操作:
char *pBuffer = new char[1024];
请注意,尽管这是不正确的:
//This is incorrect - may delete only one element, may corrupt the heap, or worse...
delete pBuffer;
相反,您应该在删除数据数组时执行此操作:
//This deletes all items in the array
delete[] pBuffer;
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
#5楼
仅将malloc
和free
用于分配将由以c为中心的库和API管理的内存。 对您控制的所有内容使用new
和delete
(以及[]
变体)。
#6楼
如果您有C代码,想移植到C ++,则可以在其中保留所有malloc()调用。 对于任何新的C ++代码,我建议改用new。
#7楼
始终在C ++中使用new。 如果需要一块无类型的内存,则可以直接使用运算符new:
void *p = operator new(size);
...
operator delete(p);
#8楼
[16.4]为什么我应该使用new而不是值得信赖的旧malloc()?
常见问题解答:new / delete调用构造函数/析构函数; new是类型安全的,malloc不是; new可以被类覆盖。
FQA:FAQ中提到的new的优点不是优点,因为构造函数,析构函数和运算符重载是垃圾(请参阅没有垃圾回收时会发生什么?),并且类型安全问题在这里确实很小(通常您有强制将malloc返回的void *转换为正确的指针类型,以将其分配给类型化的指针变量,这可能很烦人,但远非“不安全”)。
哦,使用值得信赖的旧版malloc可以使用同样值得信赖的旧版realloc。 不幸的是,我们没有新的运营商续订或其他新的东西。
尽管如此,即使语言是C ++,new仍不足以证明偏离一种语言所使用的通用样式。 特别是,如果您简单地分配对象,则具有非平凡构造函数的类将以致命的方式行为异常。 那么,为什么不在整个代码中使用new? 人们很少重载运算符new,因此它可能不会过多地妨碍您。 如果它们确实超载了,您可以随时要求它们停止。
抱歉,我无法抗拒。 :)
#9楼
new
和delete
运算符可以对类和结构进行操作,而malloc
和free
仅适用于需要转换的内存块。
使用new/delete
将有助于改善代码,因为您无需将分配的内存转换为所需的数据结构。
#10楼
malloc
并没有提供一些new
功能:
-
new
通过调用对象的构造函数来构造该对象 -
new
不需要类型化分配的内存。 - 它不需要分配大量的内存,而是需要构造许多对象。
因此,如果使用malloc
,则需要显式地执行上述操作,这并不总是可行的。 另外, new
可以重载,但malloc
不能。
#11楼
如果使用的是C ++,请尝试使用new / delete代替malloc / calloc,因为它们是运算符。 对于malloc / calloc,您需要包括另一个头。 不要在同一代码中混用两种不同的语言。 他们的工作在每种方式上都是相似的,都从哈希表中的堆段中动态分配内存。
#12楼
新vs malloc()
1) new
是一个运算符 ,而malloc()
是一个函数 。
2) new
调用了构造函数 ,而malloc()
没有。
3) new
返回确切的数据类型 ,而malloc()
返回void * 。
4) new
从不返回NULL (将引发失败),而malloc()
返回NULL
5)重新分配未由new
处理的内存,而malloc()
可以
#13楼
new
将初始化该结构的默认值,并将其中的引用正确链接到其自身。
例如
struct test_s {
int some_strange_name = 1;
int &easy = some_strange_name;
}
因此, new struct test_s
将返回带有工作引用的初始化结构,而malloc的版本没有默认值,并且未初始化内部引用。
#14楼
很少考虑使用malloc / free而不是new / delete是在使用realloc进行分配然后重新分配(简单的pod类型,不是对象)时,因为在C ++中没有类似的函数可以重新分配(尽管可以使用a更多的C ++方法)。
#15楼
要回答您的问题,您应该知道malloc
和new
之间的区别 。 区别很简单:
malloc
分配内存 ,而new
分配内存并调用要为其分配内存的对象的构造函数 。
因此,除非限于C语言,否则永远不要使用malloc,尤其是在处理C ++对象时。 那将是破坏程序的秘诀。
另外free
和delete
之间的区别是相同的。 区别在于, delete
除了释放内存外,还将调用对象的析构函数。
#16楼
仅当对象的生存期与创建对象的作用域不同时才需要动态分配(这同样适用于使作用域变小或变大),并且您有特定的原因不按值存储它工作。
例如:
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);
从C ++ 11开始,我们有std::unique_ptr
用于处理分配的内存,其中包含分配的内存的所有权。 std::shared_ptr
是为您必须共享所有权而创建的。 (您所需要的比您在一个好的程序中所期望的要少)
创建实例变得非常容易:
auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::make_unique<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::make_unique<Class[]>(new Class[](42)); // C++11
C ++ 17还添加了std::optional
,可以防止您需要内存分配
auto optInstance = std::optional<Class>{};
if (condition)
optInstance = Class{};
一旦“实例”超出范围,内存就会被清理。 转让所有权也很容易:
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)
那么什么时候还需要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
在C ++ 98/03中,您必须执行手动内存管理。 如果是这种情况,请尝试升级到该标准的最新版本。 如果您被卡住:
auto instance = new Class(); // Allocate memory
delete instance; // Deallocate
auto instances = new Class[42](); // Allocate memory
delete[] instances; // Deallocate
确保正确跟踪所有权,以确保没有任何内存泄漏! 移动语义也不起作用。
那么,什么时候我们需要C ++中的malloc? 唯一有效的原因是分配内存,然后稍后通过放置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
即使上述内容是有效的,也可以通过new-operator来完成。 std::vector
就是一个很好的例子。
最后,我们仍然在房间里放大象: C
。 如果必须使用C库来在C ++代码中分配内存,并在C代码中释放内存(或者相反),则必须使用malloc / free。
如果是这种情况,请忽略虚拟函数,成员函数,类……仅允许其中包含POD的结构。
规则的一些例外:
- 您正在使用适当的malloc编写具有高级数据结构的标准库
- 您必须分配大量内存(在10GB文件的内存副本中?)
- 您的工具阻止了您使用某些构造
- 您需要存储不完整的类型
#17楼
从较低的角度看,new将在提供内存之前初始化所有内存,而malloc将保留内存的原始内容。
#18楼
简短的答案是:在没有充分理由的情况下,请勿将malloc
用于C ++。 与C ++一起使用时, malloc
有许多缺陷,这些缺陷是new
定义的。
C ++代码的新增功能修复的缺陷
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 }
比这更糟。 如果所讨论的类型是POD(普通旧数据),则可以像第一个示例中的
f2
一样,半明智地使用malloc
为它分配内存。如果类型是POD,则不是很明显。 一个重要的因素是给定类型可能从POD更改为非POD,而不会导致编译器错误,并且可能很难调试问题。 例如,如果某人(可能是另一个程序员,在维护期间,后来又进行了更改,导致
foo
不再是POD),那么在编译时就不会出现您希望的明显错误,例如:struct foo { double d[5]; virtual ~foo() { } };
会使
f2
的malloc
也变坏,而没有任何明显的诊断。 这里的示例很简单,但是有可能在更远的地方意外地引入了非POD(例如,在基类中,通过添加非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))); }
如果分配失败,则
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; }
从根本上说,
malloc
是C的功能,而new
是C ++的功能。 结果,malloc
在构造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(); }
我们的
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)...); } };
现在,尽管解决了迄今为止我们发现的所有问题,我们实际上已经重新发明了默认的
new
运算符。 如果要使用malloc
和new
放置,那么最好还是使用new
作为开始!
#19楼
malloc
和new
之间有一个很大的区别。 malloc
分配内存。 这对于C很好,因为在C中,内存块是一个对象。
在C ++中,如果您不处理POD类型(类似于C类型),则必须在内存位置上调用构造函数以实际在其中具有对象。 非POD类型在C ++中非常常见,因为许多C ++功能使对象自动成为非POD对象。
new
分配内存并在该内存位置创建一个对象。 对于非POD类型,这意味着调用构造函数。
如果您执行以下操作:
non_pod_type* p = (non_pod_type*) malloc(sizeof *p);
您获得的指针不能取消引用,因为它没有指向对象。 您必须先在其上调用构造函数,然后才能使用它(这可以通过放置new
来完成)。
另一方面,如果您这样做:
non_pod_type* p = new non_pod_type();
您将获得始终有效的指针,因为new
创建了一个对象。
即使对于POD类型,两者之间也存在显着差异:
pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;
由于未初始化由malloc
创建的POD对象,因此这段代码将输出未指定的值。
使用new
,您可以指定要调用的构造函数,从而获得定义良好的值。
pod_type* p = new pod_type();
std::cout << p->foo; // prints 0
如果确实需要,则可以使用new
获取未初始化的POD对象。 有关更多信息,请参见此其他答案 。
另一个区别是失败时的行为。 当分配内存失败时, malloc
返回一个空指针,而new
抛出异常。
前者要求您在使用它之前测试返回的每个指针,而后者则将始终产生有效的指针。
由于这些原因,在C ++代码中,您应该使用new
而不是malloc
。 但是即使那样,您也不应使用new
“公开”方式,因为它获取了以后需要释放的资源。 使用new
,应立即将其结果传递到资源管理类中:
std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak