ChatGPT解决这个技术问题 Extra ChatGPT

在什么情况下我使用 malloc 和/或 new?

我在 C++ 中看到有多种分配和释放数据的方法,我知道当您调用 malloc 时,您应该调用 free,而当您使用 new 运算符时,您应该与 delete 配对,这是一个错误将两者混合(例如,在使用 new 运算符创建的东西上调用 free()),但我不清楚何时应该使用 malloc/ free 以及何时应该使用 new/ delete 在我的真实世界程序中。

如果您是 C++ 专家,请告诉我您在这方面遵循的任何经验法则或约定。

我只想补充一点,你不能混合使用这两种样式——也就是说,你不能使用 new 创建一个对象然后对其调用 free(),也不能尝试删除 malloc() 分配的块。说起来可能很明显,但尽管如此......
好的答案,我要补充的(我还没有看到)是 new/delete 为你调用构造函数/析构函数, malloc/free 没有。只是一个值得一提的区别。
对于现代 C++,我仍在尝试寻找使用其中任何一个的理由。
或者两者都不使用并使用 std:shared_ptr。然后你根本不必删除。
std::unique_ptr 应该是默认选择,而不是 std::shared_ptr

c
cmaher

除非您被迫使用 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/freemalloc/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

关于在你应该调用 delete []foo 时调用 delete foo,一些编译器会自动为你修复这个问题并且不会泄漏,而其他只会删除第一个条目并泄漏。我在一些代码中有一些这些,valgrind 会为你找到它们。
如果您不使用正确的删除,则结果是未定义的。这是不正确的。有时它可能使部分事情正确或工作的事实只是盲目的运气。
@KPexEA:即使某些编译器可能会修复您的错误,但首先制作它们仍然是错误的 :) 始终在适当的地方使用 delete[] 。
“除非你被迫使用 C,否则你永远不应该使用 malloc。总是使用 new。”为什么?这里的胜利是什么?对于我们需要构造的对象,但对于内存块,您清楚地记录了两种导致编码错误的方法(更容易在 new 中捕获 () 与 [] 以及更不容易捕获不匹配数组与缩放器 new 和 delete)。对原始内存块使用 new/delete 的动机是什么?
@DeadMG:如果要创建一个供异步 API 函数使用的数组,new[] 不会比 std::vector 更安全吗?如果使用 new[],则指针变为无效的唯一方法是通过显式 delete,而为 std::vector 分配的内存可能会在向量调整大小或离开范围时失效。 (请注意,当使用 new[] 时,如果异步方法仍处于挂起状态,则必须考虑可能无法调用 delete 的可能性;如果可能需要放弃异步操作,则可能必须通过回调安排删除)。
C
Community

简短的回答是:不要在没有充分理由的情况下将 malloc 用于 C++。 malloc 与 C++ 一起使用时存在许多缺陷,new 旨在克服这些缺陷。

新的 C++ 代码修复的缺陷

malloc 在任何意义上都不是类型安全的。在 C++ 中,您需要从 void* 转换返回值。这可能会引入很多问题:#include struct foo { double d[5]; }; int main() { foo *f1 = malloc(1); // 错误,没有强制转换 foo *f2 = static_cast(malloc(sizeof(foo))); foo *f3 = static_cast(malloc(1)); // 没有错误,很糟糕 } 不过比这更糟糕。如果所讨论的类型是 POD(普通旧数据),那么您可以半明智地使用 malloc 为其分配内存,就像 f2 在第一个示例中所做的那样。虽然类型是 POD,但它并不那么明显。给定类型有可能从 POD 更改为非 POD,而不会导致编译器错误并且可能很难调试问题,这一事实是一个重要因素。例如,如果某人(可能是另一个程序员,在维护期间,很久以后进行了导致 foo 不再是 POD 的更改,那么在编译时不会像您希望的那样出现明显的错误,例如:struct foo { double d [5]; virtual ~foo() { } }; 会使 f2 的 malloc 也变坏,没有任何明显的诊断。这里的例子很简单,但有可能在更远的地方意外引入非 POD(例如在基类,通过添加一个非 POD 成员)。如果你有 C++11/boost,你可以使用 is_pod 检查这个假设是否正确,如果不正确则产生错误:#include #include foo *safe_foo_malloc() { static_assert(std::is_pod::value, "foo must be POD"); return static_cast(malloc(sizeof(foo))); } 虽然 boost 无法确定一个类型是否是没有 C++11 或其他编译器扩展的 POD。如果分配失败,malloc 返回 NULL。new 将抛出 std::bad_alloc。稍后使用 NULL 的行为指针未定义。异常在抛出时具有清晰的语义,并且是从错误源抛出的。在每次调用时用适当的测试包装 malloc 似乎很乏味且容易出错。 (您只需忘记一次即可撤消所有出色的工作)。可以允许异常传播到调用者能够合理处理它的级别,因为 NULL 更难以有意义地传回。我们可以扩展我们的 safe_foo_malloc 函数来抛出异常或退出程序或调用一些处理程序: #include #include void my_malloc_failed_handler(); foo *safe_foo_malloc() { static_assert(std::is_pod::value, "foo 必须是 POD"); foo *mem = static_cast(malloc(sizeof(foo))); if (!mem) { my_malloc_failed_handler(); // 或 throw ... } return mem; } 从根本上说 malloc 是一个 C 特性,而 new 是一个 C++ 特性。结果 malloc 不能很好地与构造函数配合使用,它只着眼于分配一块字节。我们可以进一步扩展我们的 safe_foo_malloc 以使用放置 new:#include #include void my_malloc_failed_handler(); foo *safe_foo_malloc() { void *mem = malloc(sizeof(foo)); if (!mem) { my_malloc_failed_handler(); // 或抛出 ... } return new (mem)foo();我们的 safe_foo_malloc 函数不是很通用——理想情况下,我们想要可以处理任何类型的东西,而不仅仅是 foo。我们可以使用非默认构造函数的模板和可变参数模板来实现这一点:#include #include #include void my_malloc_failed_handler();模板 struct alloc { 模板 静态 T *safe_malloc(Args&&... args) { void *mem = malloc(sizeof(T)); if (!mem) { my_malloc_failed_handler(); // 或抛出 ... } return new (mem)T(std::forward(args)...); } };现在,尽管在解决我们迄今为止发现的所有问题时,我们实际上已经重新发明了默认的 new 运算符。如果您要使用 malloc 和 Placement new,那么您不妨使用 new 开始!


太糟糕了 C++ 使 structclass 的意思基本相同;我想知道将 struct 保留给 POD 并可能将所有 class 类型假定为非 POD 是否会有任何问题。由 C++ 发明之前的代码定义的任何类型都必然是 POD,因此我认为向后兼容性不会成为问题。将非 POD 类型声明为 struct 而不是 class 是否有优势?
@supercat 有点晚了,但事实证明,让 structclass 做几乎相同的事情是一个很棒的设计决策,现在启用了一个名为 "metaclasses" (from Herb) 的简洁功能。
@Rakete1111:乍一看,该提案似乎预处理了使用美元前缀关键字(如 $class)的语言版本。但是,我不确定这与 classstruct 是同义词有什么关系。
@supercat 类型系统会更加分叉。通过让 classstruct 实际上表示相同的东西,您可以对它们进行任意转换 ($class),而不必担心将 class 变成 struct,反之亦然。
@Rakete1111:如果某些类型的操作和转换对某些类型是安全的,但对其他类型不安全,那么让类型直接识别它,并让编译器拒绝不安全的操作和转换,似乎比更改用于的元类更好仅适用于 PODS 的方式,悄悄地更改为非 PODS。
M
Matthias Benkard

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++ 的偏见。 C++ 是一种用于创建面向性能的软件的语言,垃圾收集器只会对其目标有害。我不同意你的整个答案!
@Miguel 你错过了这个笑话。
F
Ferruccio

始终在 C++ 中使用 new。如果你需要一块无类型的内存,你可以直接使用 operator new:

void *p = operator new(size);
   ...
operator delete(p);

有趣的是,当我需要这样的原始数据缓冲区时,我总是只分配一个 unsigned char 数组。
小心语义应该是这样的: p_var = new type(initializer);不是大小。
如果您直接调用 operator new ,则不会将要分配的字节数作为参数。
Hrm 不确定,我从未听说过这种语法。
operator new 的反义词是 operator delete。对类型为 void* 的表达式调用 delete 不是一个定义明确的操作。
Y
Yogeesh H T

新 vs malloc()

1) new运算符,而 malloc()函数

2) new 调用 构造函数,而 malloc() 不调用。

3) new 返回精确数据类型,而 malloc() 返回void *

4) new 从不返回 NULL(将抛出失败),而 malloc() 返回 NULL

5) new 未处理的内存重新分配,而 malloc() 可以


嗨,对于第 4) 点,可以指示 new 在失败时返回 NULL。 char* ptr = new (std::nothrow) char [323232];
6) new 从构造函数参数创建,而 malloc 使用大小。
还有一个 new 函数
如果您在 C 中如此倾向于 重新分配,我希望您使用 realloc 而不是 malloc,并从初始化为 NULL 的指针变量开始。另一方面,如果您想要 C++ 中的 resizable 内存块,我建议使用 std::vector 而不是 realloc... 那或文件。
d
dmckee --- ex-moderator kitten

mallocfree only 用于分配将由以 c 为中心的库和 API 管理的内存。对您控制的所有内容使用 newdelete(以及 [] 变体)。


另请注意,编写良好的 C 库将在内部隐藏 malloc 和 free,这就是 C 程序员应该工作的方式。
@dmckee 你有使用 malloc 和 free 以 c 为中心的库的 C++ 示例吗?
@Dacav:如果 C 函数将接受指向在函数返回后需要继续使用的对象的指针,并且调用者将无法知道何时仍需要该对象,那么该函数将是完全合理的指定必须使用 malloc 创建指针。同样,如果像 strdup 这样的函数需要创建一个对象并将其返回给调用者,那么指定调用者必须在不再需要对象时调用 free 是完全合理的。这样的函数如何避免将它们对 malloc/free 的使用暴露给调用者?
@supercat,让C函数接受指向对象的指针本质上是错误的,因为C根本不知道对象。一般来说,我认为最好的方法是在 C 中也有围绕分配/解除分配的语义包装器。如果 C 库要求调用者预分配和/或解除分配内存,它仍然可以接受,但不太灵活。如果 C 函数正在执行此操作并声明对已分配内存的所有权,则隐式要求您使用 malloc 分配它。
@Dacav 从 C++ 标准的 The C++ object model 部分,我们可以看到 object 的定义:“对象是存储区域。” 有一个类似的定义在 C 标准中; char c; 在 C 和 C++ 中,此变量表示一个对象。不同之处在于C++ 中的一些(但不是全部)对象是也是 多态的(毕竟C++ 是面向对象的)。不要误以为只有面向对象的代码才能使用对象。
T
The Quantum Physicist

要回答您的问题,您应该知道mallocnew 之间的区别。区别很简单:

malloc 分配内存,而new 分配内存并调用您为其分配内存的对象的构造函数

因此,除非您仅限于使用 C,否则您永远不应该使用 malloc,尤其是在处理 C++ 对象时。那将是破坏您的程序的秘诀。

freedelete 之间的区别也完全相同。不同之处在于 delete 除了释放内存外,还会调用对象的析构函数。


3
3 revs

mallocnew 之间有一个很大的区别。 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 对象。有关这方面的更多信息,请参阅 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

J
JVApen

仅当对象的生命周期应与其创建的范围不同时才需要动态分配(这也适用于使范围变小或变大)并且您有特定原因不按值存储它工作。

例如:

 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 的结构。

规则的一些例外情况:

您正在使用适合 malloc 的高级数据结构编写标准库

您必须分配大量内存(在 10GB 文件的内存副本中?)

您有工具阻止您使用某些构造

您需要存储不完整的类型


绝对+1! New/delete 和 malloc/free 是为我们的祖父母准备的。这些不是在现代 C++ 中执行动态分配的建议方法。堆栈上的对象和容器、unique_ptr 和 shared_ptr 是处理对象的“正确”方式。我有包含 100,000 行代码的示例项目,没有 mallocnew 调用。
h
herohuyongtao

new 做了一些 malloc 没有做的事情:

new 通过调用该对象的构造函数来构造对象 new 不需要对分配的内存进行类型转换。它不需要分配大量内存,而是需要构造多个对象。

因此,如果您使用 malloc,那么您需要明确地执行上述操作,这并不总是实用的。此外,new 可以重载,但 malloc 不能。


P
PSkocik

如果您使用不需要构造/销毁且需要重新分配的数据(例如,大量整数数组),那么我相信 malloc/free 是一个不错的选择,因为它为您提供了 realloc,这比 new-memcpy 快得多-delete(它在我的 Linux 机器上,但我想这可能取决于平台)。如果您使用非 POD 且需要构造/销毁的 C++ 对象,则必须使用 new 和 delete 运算符。

无论如何,我不明白为什么你不应该同时使用两者(前提是你释放你分配的内存并删除分配给新的对象)如果可以利用速度提升(如果你正在重新分配大型数组,有时是一个重要的提升)的POD)realloc可以给你。

除非你需要它,否则你应该坚持使用 C++ 中的 new/delete。


P
Pang

如果您使用 C++,请尝试使用 new/delete 而不是 malloc/calloc,因为它们是运算符。对于 malloc/calloc,您需要包含另一个标头。不要在同一代码中混合使用两种不同的语言。他们的工作在各个方面都相似,都从哈希表中的堆段动态分配内存。


F
Fred Larson

如果您有想要移植到 C++ 的 C 代码,您可能会在其中留下任何 malloc() 调用。对于任何新的 C++ 代码,我建议使用 new 代替。


k
kungfooman

new 将初始化结构的默认值并将其中的引用正确链接到自身。

例如

struct test_s {
    int some_strange_name = 1;
    int &easy = some_strange_name;
}

所以 new struct test_s 将返回一个带有工作引用的初始化结构,而 malloc 的版本没有默认值,并且内部引用没有初始化。


P
Peiti Li

从较低的角度来看,new 将在给予内存之前初始化所有内存,而 malloc 将保留内存的原始内容。


new 通常不会初始化内存,尽管有一些方法可以做到这一点:有关它的讨论,请参见 stackoverflow.com/questions/2204176/…
m
moribvndvs

在下面的场景中,我们不能使用 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;
    }
};

P
Pang

考虑使用 malloc/free 而不是 new/delete 的罕见情况是,当您使用 realloc 进行分配然后重新分配(简单的 pod 类型,而不是对象)时,因为在 C++ 中没有与 realloc 类似的功能(尽管可以使用更多 C++ 方法)。


h
hackjutsu

newdelete 运算符可以对类和结构进行操作,而 mallocfree 仅适用于需要强制转换的内存块。

使用 new/delete 将有助于改进您的代码,因为您不需要将分配的内存转换为所需的数据结构。


H
Hitesh Ahuja

malloc() 用于在 C 中动态分配内存,而在 c++ 中由 new() 完成相同的工作。因此,您不能混合使用 2 种语言的编码约定。如果你问 calloc 和 malloc() 之间的区别会很好


可以(但几乎总是不应该)在 C++ 中使用 malloc
您还错过了应该避免动态内存分配的主要观点,除非通过智能指针这样做。否则,您只是在让自己承受痛苦