我在 C++ 中看到有多种分配和释放数据的方法,我知道当你调用时malloc
你应该调用free
,当你使用new
操作符时你应该配对,delete
将两者混合是错误的(例如调用free()
创建的东西与new
操作员),但我不清楚何时应该在我的现实世界的程序中使用malloc
/free
以及何时应该使用new
/ 。delete
如果您是 C++ 专家,请告诉我您在这方面遵循的任何经验法则或约定。
我在 C++ 中看到有多种分配和释放数据的方法,我知道当你调用时malloc
你应该调用free
,当你使用new
操作符时你应该配对,delete
将两者混合是错误的(例如调用free()
创建的东西与new
操作员),但我不清楚何时应该在我的现实世界的程序中使用malloc
/free
以及何时应该使用new
/ 。delete
如果您是 C++ 专家,请告诉我您在这方面遵循的任何经验法则或约定。
除非你被迫使用 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;
关键字是 C++的new
执行方式,它将确保您的类型的构造函数名为. 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
简短的回答是:不要在没有malloc
充分理由的情况下使用 C++。malloc
与 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
}
虽然比这更糟。如果有问题的类型是POD(普通旧数据) ,那么您可以像第一个示例中那样,半明智地使用malloc
它来为其分配内存。f2
虽然类型是 POD,但它并不那么明显。给定类型有可能从 POD 更改为非 POD,而不会导致编译器错误并且可能很难调试问题,这一事实是一个重要因素。例如,如果某人(可能是另一个程序员,在维护期间,很久以后要进行导致foo
不再是 POD 的更改,那么在编译时不会像您希望的那样出现明显的错误,例如:
struct foo {
double d[5];
virtual ~foo() { }
};
在没有任何明显诊断的情况下,也会使malloc
of变坏。f2
此处的示例很简单,但可能会在更远的地方意外引入非 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)));
}
尽管 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;
}
从根本上说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();
}
我们的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
开始!
来自C++ FQA Lite:
[16.4] 为什么我应该使用new而不是值得信赖的旧malloc()?
FAQ:new/delete调用构造函数/析构函数;new 是类型安全的, malloc 不是;new 可以被一个类覆盖。
FQA:FAQ 中提到的 new 的优点不是优点,因为构造函数、析构函数和操作符重载都是垃圾(看看没有垃圾收集会发生什么?),类型安全问题在这里真的很小(通常你有将 malloc 返回的 void* 转换为正确的指针类型,以将其分配给类型化的指针变量,这可能很烦人,但远非“不安全”)。
哦,使用可信赖的旧 malloc 可以使用同样可信赖的旧 realloc。太糟糕了,我们没有闪亮的新运营商更新或其他东西。
尽管如此,new 还不足以证明偏离整个语言使用的通用风格是合理的,即使该语言是 C++ 也是如此。特别是,如果您只是 malloc 对象,则具有非平凡构造函数的类将以致命的方式行为不端。那么为什么不在整个代码中使用 new 呢?人们很少重载 operator new,所以它可能不会妨碍你太多。如果他们确实超载了新的,你可以随时要求他们停下来。
对不起,我无法抗拒。:)
始终在 C++ 中使用 new。如果你需要一块无类型的内存,你可以直接使用 operator new:
void *p = operator new(size);
...
operator delete(p);
新 vs malloc()
1)new
是运算符,whilemalloc()
是函数。
2)new
调用构造函数,而malloc()
没有。
3)new
返回确切的数据类型,同时malloc()
返回void *。
4)在返回 NULL时new
从不返回NULL(将抛出失败)malloc()
new
5) whilemalloc()
可以重新分配未处理的内存
malloc
仅用于分配将由以 c 为中心的库和 API 管理的内存。对您控制的所有内容使用和(以及变体)。free
new
delete
[]
要回答您的问题,您应该知道和之间的区别malloc
new
。区别很简单:
malloc
分配内存,同时分配内存并调用您为其分配内存的对象new
的构造函数。
因此,除非您仅限于使用 C,否则您永远不应该使用 malloc,尤其是在处理 C++ 对象时。那将是破坏您的程序的秘诀。
free
和之间的区别delete
也完全一样。不同之处在于delete
,除了释放内存之外,它还会调用对象的析构函数。
malloc
和之间有一个很大的区别new
。malloc
分配内存。这对 C 来说很好,因为在 C 中,一块内存就是一个对象。
在 C++ 中,如果您不处理 POD 类型(类似于 C 类型),则必须在内存位置调用构造函数才能在其中实际拥有对象。非 POD 类型在 C++ 中非常常见,因为许多 C++ 特性使对象自动成为非 POD。
new
分配内存并在该内存位置创建一个对象。对于非 POD 类型,这意味着调用构造函数。
如果你做这样的事情:
non_pod_type* p = (non_pod_type*) malloc(sizeof *p);
您获得的指针无法取消引用,因为它不指向对象。您需要先调用它的构造函数,然后才能使用它(这是使用 placement 完成的new
)。
另一方面,如果您这样做:
non_pod_type* p = new non_pod_type();
你得到一个始终有效的指针,因为new
创建了一个对象。
即使对于 POD 类型,两者之间也存在显着差异:
pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;
这段代码将打印一个未指定的值,因为由其创建的 POD 对象malloc
未初始化。
使用new
,您可以指定要调用的构造函数,从而获得明确定义的值。
pod_type* p = new pod_type();
std::cout << p->foo; // prints 0
如果你真的想要它,你可以使用 usenew
来获取未初始化的 POD 对象。有关更多信息,请参阅this other answer。
另一个区别是失败时的行为。当它分配内存失败时,malloc
返回一个空指针,同时new
抛出一个异常。
前者要求您在使用它之前测试返回的每个指针,而后者总是会产生有效的指针。
由于这些原因,在 C++ 代码中您应该使用new
,而不是malloc
. 但即便如此,您也不应该使用new
“公开”,因为它会获取您需要稍后发布的资源。使用new
时,应立即将其结果传递给资源管理类:
std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
仅当对象的生命周期与创建它的范围不同时才需要动态分配(这也适用于使范围变小或变大)并且您有特定的原因不按值存储它工作。
例如:
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::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
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 呢?唯一有效的原因是分配内存并稍后通过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
即使以上是有效的,这也可以通过 new-operator 来完成。std::vector
是一个很好的例子。
最后,我们还有房间里的大象:C
。如果您必须使用在 C++ 代码中分配内存并在 C 代码中释放(或相反)的 C 库,您将被迫使用 malloc/free。
如果你在这种情况下,忘记虚函数、成员函数、类......只允许其中包含 POD 的结构。
规则的一些例外情况:
有一些事情new
不会malloc
:
new
通过调用该对象的构造函数来构造该对象new
不需要对分配的内存进行类型转换。所以,如果你使用malloc
,那么你需要明确地做上面的事情,这并不总是实用的。此外,new
可以重载但malloc
不能重载。
如果您使用不需要构造/销毁且需要重新分配的数据(例如,大量整数数组),那么我相信 malloc/free 是一个不错的选择,因为它为您提供了 realloc,这比 new-memcpy 快得多-delete(它在我的 Linux 机器上,但我想这可能取决于平台)。如果您使用非 POD 且需要构造/销毁的 C++ 对象,则必须使用 new 和 delete 运算符。
无论如何,我不明白为什么你不应该同时使用两者(前提是你释放你分配的内存并删除分配给新的对象)如果可以利用速度提升(如果你正在重新分配大型数组,有时是一个重要的提升)的POD)realloc可以给你。
除非你需要它,否则你应该坚持使用 C++ 中的 new/delete。
如果您使用 C++,请尝试使用 new/delete 而不是 malloc/calloc,因为它们是运算符。对于 malloc/calloc,您需要包含另一个标头。不要在同一代码中混合使用两种不同的语言。他们的工作在各个方面都相似,都从哈希表中的堆段动态分配内存。
如果您有想要移植到 C++ 的 C 代码,您可能会在其中留下任何 malloc() 调用。对于任何新的 C++ 代码,我建议使用 new 代替。
new
将初始化结构的默认值并将其中的引用正确链接到自身。
例如
struct test_s {
int some_strange_name = 1;
int &easy = some_strange_name;
}
所以new struct test_s
将返回一个带有工作引用的初始化结构,而 malloc 的版本没有默认值,并且内部引用没有初始化。
在下面的场景中,我们不能使用 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;
}
};
从较低的角度来看,new 将在给予内存之前初始化所有内存,而 malloc 将保留内存的原始内容。
new
anddelete
运算符可以对类和结构进行操作,而andmalloc
仅free
适用于需要转换的内存块。
使用new/delete
将有助于改进您的代码,因为您不需要将分配的内存转换为所需的数据结构。
顺便说一句,如果你有 malloc 的代码,请使用这个正则表达式:
搜索:
\((\w+)\*\)malloc\((\w+)\)
用。。。来代替:
new \1[\2]
考虑使用 malloc/free 而不是 new/delete 的罕见情况是,当您使用 realloc 进行分配然后重新分配(简单的 pod 类型,而不是对象)时,因为在 C++ 中没有与 realloc 类似的功能(尽管可以使用更多 C++ 方法)。
malloc() 用于在 C 中动态分配内存,而在 c++ 中由 new() 完成相同的工作。因此,您不能混合使用 2 种语言的编码约定。如果你问 calloc 和 malloc() 之间的区别会很好