提问人: 提问时间:10/9/2008 最后编辑:JVApen 更新时间:8/11/2022 访问量:354531
在什么情况下使用 malloc 和/或 new?
In what cases do I use malloc and/or new?
问:
我看到在 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
评论
new[]
std::vector
new[]
delete
std::vector
new[]
delete
仅将 malloc
和 free
用于分配将由以 c 为中心的库和 API 管理的内存。对您控制的所有内容使用 new
和 delete
(以及 []
变体)。
评论
malloc
strdup
free
char c;
如果你有 C 代码要移植到 C++,你可以在其中保留任何 malloc() 调用。对于任何新的 C++ 代码,我建议改用 new。
始终在 C++ 中使用 new。如果你需要一个非类型化内存块,你可以直接使用运算符 new:
void *p = operator new(size);
...
operator delete(p);
评论
operator new
operator delete
delete
void*
从 C++ FQA 精简版:
[16.4] 为什么我应该使用 new 而不是 值得信赖的老 malloc()?
FAQ:新建/删除调用 构造函数/析构函数;new 是类型 安全,Malloc 不安全;新的可以是 被类覆盖。
FQA:新提到的优点 常见问题解答不是美德,因为 构造函数、析构函数和 运算符重载是垃圾(请参阅 当你没有垃圾时会发生什么 集合?)和类型安全 这里的问题真的很小(通常 你必须施放 malloc 到正确的指针类型 将其分配给类型化指针变量, 这可能很烦人,但远非如此 “不安全”)。
哦,使用值得信赖的旧 malloc 使得使用平等成为可能 值得信赖和老雷洛克。可惜我们 不要让闪亮的新操作员续约什么的。
不过,新的还不够糟糕 证明偏离共同点是合理的 在整个语言中使用的样式,甚至 当语言为 C++ 时。在 特别是,具有非平凡的类 构造函数在致命中会行为不端 如果您只是对对象进行恶意定位,则会采取各种方式。 那么为什么不在整个 法典?人们很少超载操作员 新的,所以它可能不会进入你的 太多了。如果他们确实超载 新的,你可以随时要求他们停下来。
对不起,我实在忍不住了。:)
评论
and 运算符可以对类和结构进行操作,而 and 只能处理需要强制转换的内存块。new
delete
malloc
free
使用将有助于改进代码,因为您不需要将分配的内存转换为所需的数据结构。new/delete
从较低的角度来看,new 将在提供内存之前初始化所有内存,而 malloc 将保留内存的原始内容。
评论
简短的回答是:如果没有充分的理由,不要用于C++。 与 C++ 一起使用时有许多缺陷,这些缺陷被定义为克服。malloc
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(普通旧数据),那么您可以半明智地使用它来为其分配内存,就像在第一个示例中一样。
malloc
f2
但是,如果类型是 POD,则不那么明显。给定类型有可能从 POD 更改为非 POD,而不会产生编译器错误,并且可能很难调试问题,这是一个重要因素。例如,如果有人(可能是另一个程序员,在维护期间,很久以后)进行更改,导致不再是 POD,那么编译时不会像您希望的那样出现明显的错误,例如:
foo
struct foo { double d[5]; virtual ~foo() { } };
会使 OF 也变得糟糕,没有任何明显的诊断。这里的示例很简单,但可能会意外地在更远的地方引入非 PODness(例如,在基类中,通过添加非 POD 成员)。如果你有 C++11/boost,你可以用它来检查这个假设是否正确,如果不是,则产生错误:
malloc
f2
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; }
从根本上说,它是一个 C 功能,并且是一个 C++ 功能。因此,它不能很好地与构造函数配合使用,它只考虑分配一个字节块。我们可以进一步扩展我们的使用放置:
malloc
new
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
评论
struct
class
struct
class
struct
class
struct
class
$class
class
struct
class
struct
$class
class
struct
和 之间有一个很大的区别。 分配内存。这对 C 来说很好,因为在 C 中,内存块是一个对象。malloc
new
malloc
在 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
如果确实需要它,可以使用 use 来获取未初始化的 POD 对象。有关更多信息,请参阅其他答案。new
另一个区别是失败时的行为。当它无法分配内存时,返回一个空指针,同时抛出一个异常。malloc
new
前者要求您在使用之前测试返回的每个指针,而后者将始终生成有效的指针。
由于这些原因,在 C++ 代码中,您应该使用 ,而不是 .但即便如此,您也不应该使用“公开”,因为它会获取您以后需要发布的资源。使用时,应立即将其结果传递到资源管理类中:new
malloc
new
new
std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
malloc() 用于在 C 中动态分配内存 而 C++ 中的 new() 也完成了同样的工作。 因此,您不能混合使用 2 种语言的编码约定。 如果您询问 calloc 和 malloc() 之间的区别,那就太好了
评论
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;
}
};
如果您使用不需要构造/销毁并且需要重新分配的数据(例如,大量 int),那么我相信 malloc/free 是一个不错的选择,因为它为您提供了 realloc,这比 new-memcpy-delete 快得多(它在我的 Linux 机器上,但我想这可能取决于平台)。如果使用非 POD 且需要构造/销毁的 C++ 对象,则必须使用 new 和 delete 运算符。
无论如何,我不明白为什么你不应该同时使用两者(前提是你释放你的错位内存并删除用 new 分配的对象),如果可以利用速度提升(有时是一个显着的提升,如果你正在重新分配大型 POD 数组)可以给你。
除非你需要它,否则你应该坚持在 C++ 中使用新建/删除。
有几件事是做不到的:new
malloc
new
通过调用该对象的构造函数来构造该对象new
不需要对分配的内存进行类型转换。- 它不需要分配一定数量的内存,而是需要一些 要构造的对象。
所以,如果你使用 ,那么你需要明确地做上面的事情,这并不总是实用的。此外,可以重载,但不能重载。malloc
new
malloc
如果您使用的是 C++,请尝试使用 new/delete 而不是 malloc/calloc,因为它们是运算符。对于 malloc/calloc,您需要包含另一个标头。不要在同一代码中混合使用两种不同的语言。它们的工作在各个方面都是相似的,都从哈希表中的堆段动态分配内存。
新与 malloc()
1) 是运算符,while 是函数。new
malloc()
2) 调用构造函数,而不调用。new
malloc()
3) 返回确切的数据类型,而返回 void *。new
malloc()
4) 从不返回 NULL(失败时会抛出),而返回 NULLnew
malloc()
5) 重新分配 while can 未处理的内存new
malloc()
评论
char* ptr = new (std::nothrow) char [323232];
new
realloc
malloc
NULL
std::vector
realloc
new
将初始化结构的默认值,并正确地将其中的引用链接到其自身。
例如
struct test_s {
int some_strange_name = 1;
int &easy = some_strange_name;
}
因此将返回一个带有工作引用的初始化结构,而 malloc 的版本没有默认值,并且实习生引用没有初始化。new struct test_s
考虑使用 malloc/free 而不是 new/delete 的极少数情况是使用 realloc 分配然后重新分配(简单的 pod 类型,而不是对象),因为 C++ 中没有与 realloc 类似的函数(尽管这可以使用更多的 C++ 方法完成)。
要回答您的问题,您应该知道 malloc
和 new
之间的区别。区别很简单:
malloc
分配内存,同时分配内存并调用要为其分配内存的对象的构造函数。new
因此,除非您仅限于 C,否则永远不应该使用 malloc,尤其是在处理 C++ 对象时。这将是破坏程序的秘诀。
和之间的区别也完全相同。不同之处在于,除了释放内存之外,还会调用对象的析构函数。free
delete
delete
仅当对象的生存期应与在其中创建对象的范围不同时,才需要动态分配(这也适用于使范围变小和变大),并且您有特定的原因,即按值存储它不起作用。
例如:
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++ 还添加了可以防止您需要内存分配的功能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)
那么你什么时候还需要呢?几乎从未从 C++11 开始。大多数情况下,您都会使用,直到您遇到通过原始指针转移所有权的 API。new
std::make_unique
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
尽管上述内容是有效的,但这也可以通过新运算符来完成。 就是一个很好的例子。std::vector
最后,我们房间里还有大象:.如果你必须使用一个C库,其中内存在C++代码中分配,并在C代码中释放(或相反),你被迫使用malloc/free。C
如果您在这种情况下,请忘记虚函数、成员函数、类......只允许包含 POD 的结构。
规则的一些例外情况:
- 您正在编写一个具有高级数据结构的标准库,其中 malloc 是合适的
- 您必须分配大量内存(在 10GB 文件的内存副本中?
- 你有工具阻止你使用某些构造
- 您需要存储一个不完整的类型
评论
malloc
new
我以前玩过很少用于计算机图形学的 C/C++ 应用程序。 过了这么多年,有些东西消失了,我非常想念它们。
关键是,malloc 和 new,或者 free 和 delete,可以同时工作, 特别是对于某些最常见的基本类型。
例如,char 数组,既可以用 malloc 分配,也可以用 new 分配。 主要区别在于,使用 new 可以实例化固定的数组大小。
char* pWord = new char[5]; // allocation of char array of fixed size
在这种情况下,不能使用变量来表示数组的大小。 相反,malloc 函数可以允许可变大小。
int size = 5;
char* pWord = (char*)malloc(size);
在这种情况下,可能需要转换转换运算符。 对于从 malloc 返回的类型,它是指向 void 的指针,而不是 char。 有时编译器不知道如何转换这种类型。
分配内存块后,您可以设置变量值。 对于一些较大的数组,memset 函数确实可能更慢。 但是,在分配值之前,必须先将所有咬合设置为 0。 因为数组的值可以具有任意内容。
假设该数组被分配了另一个较小大小的数组。 数组元素的一部分仍可能具有任意内容。 在这种情况下,建议调用 memset 函数。
memset((void*)pWord, 0, sizeof(pWord) / sizeof(char));
分配函数可用于所有 C 包。 因此,这些是通用函数,必须适用于更多的 C 类型。 C++ 库是旧 C 库的扩展。 因此,malloc 函数返回一个通用的 void* 指针。 这些 sructures 未定义 new 或 delete 运算符。 在这种情况下,可以使用 malloc 分配自定义变量。
new 和 delete 关键字实际上是一些定义的 C 运算符。 也许自定义联合或类可以定义这些运算符。 如果未在类中定义 new 和 delete,则它们可能不起作用。 但是,如果一个类派生自另一个类,该类具有这些运算符, new 和 delete 关键字可以具有基本类行为。
关于释放数组,free 只能与 malloc 配对使用。 不能使用 malloc 分配变量,然后使用 delete 释放变量。
简单的 delete 运算符仅引用数组的第一项。 因为 pWord 数组也可以写成:
pWord = &pWord[0]; // or *pWord = pWord[0];
当必须删除数组时,请改用 delete[] 运算符:
delete[] pWord;
强制转换还不错,只是不适用于所有变量类型。 转换强制转换也是一个运算符函数,必须定义。 如果未为特定类型定义此运算符,则它可能无法正常工作。 但并非所有错误都是由于此转换转换运算符造成的。
此外,在使用免费调用时,必须使用对 void 指针的强制转换。 这是因为 free 函数的参数是一个 void 指针。
free((void*)pWord);
由于数组的大小太小,可能会出现一些错误。 但这是另一回事了,不是因为使用了演员阵容。
亲切的问候, 阿德里安·布里纳斯
评论