在什么情况下我使用malloc vs new?

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/ 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
Run Code Online (Sandbox Code Playgroud)

  • "除非你被迫使用C,否则你永远不应该使用malloc.总是使用新的." 为什么?这里的胜利是什么?对于我们需要构造的对象,但是对于内存块,你清楚地记录了两种编码错误的方法(在new中更容易捕获()vs []和不太容易捕获的不匹配数组vs scaler new和delete).对原始内存块使用new/delete的动机是什么? (54认同)
  • 请注意"永远不要使用X!" 没有解释原因. (44认同)
  • 如果您没有使用正确的删除**,结果是未定义的**.这是不正确的.事实上它可能会使事情的一部分正确或有时工作只是盲目的运气. (28认同)
  • @KPexEA:即使某些编译器可能会修复您的错误,但首先制作它们仍然是错误的:)总是在适当的地方使用delete []. (8认同)
  • @DeadMG:如果一个人正在创建一个供异步API函数使用的数组,那么`new []`不会比`std :: vector`更安全吗?如果使用`new []`,指针变为无效的唯一方法是通过显式`delete`,而为std :: vector`分配的内存在向量调整大小或离开范围时可能无效.(注意,当使用`new []`时,如果异步方法仍处于未决状态,则必须允许可能无法调用`delete`;如果可能需要放弃异步操作,则可能必须安排通过回调删除). (3认同)
  • 关于在调用delete [] foo时调用delete foo,一些编译器会自动修复这个并且不会泄漏,而其他编译器只会删除第一个条目并泄漏.我在一些代码中有一些这些,valgrind会为你找到它们. (2认同)
  • 类型安全的意思?任何人都可以详细说明吗? (2认同)
  • 永远不要使用`new []`和`delete []`,总是使用`std :: vector`. (2认同)
  • 实际上,永远不要使用“new”,但如果您确实必须使用其中之一,则更喜欢在“malloc”之前使用它。它们都需要您手动进行内存管理。在(几乎)所有情况下,您都有“std::vector”和智能指针,它们在被破坏时会释放内存。 (2认同)

Fle*_*exo 140

简短的回答是:如果没有malloc充分的理由,请不要使用C++.malloc与C++一起使用时有许多不足之处,C++ new被定义为可以克服.

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

  1. 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)
  2. 但它比这更糟糕.如果有问题的类型是POD(普通旧数据),那么你可以半合理地使用它malloc来为它分配内存,就像f2第一个例子中那样.

    如果一个类型是POD,那就不那么明显了.事实上,给定类型可能从POD更改为非POD而没有产生编译器错误并且可能非常难以调试问题是一个重要因素.例如,如果有人(可能是另一个程序员,在维护期间,很久以后要做出导致foo不再是POD 的更改,那么在编译时不会出现明显的错误,例如:

    struct foo {
      double d[5];
      virtual ~foo() { }
    };
    
    Run Code Online (Sandbox Code Playgroud)

    将使mallocf2也变得不好,没有任何明显的诊断.这里的示例是微不足道的,但是可能会意外地将非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.

  3. mallocNULL如果分配失败则返回.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)
  4. 从根本上说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)
  5. 我们的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可以开始使用!

  • C++使`struct`和`class`意味着基本相同;这太糟糕了; 我想知道是否有任何问题可以为POD保留`struct`,并且可能假定所有`class`类型都是非POD.由C++发明之前的代码定义的任何类型都必然是POD,所以我不认为向后兼容性会成为问题.将非POD类型声明为`struct`而不是`class`是否有优势? (25认同)

Mat*_*ard 51

来自C++ FQA Lite:

[16.4]为什么我应该使用new而不是值得信赖的旧malloc()?

FAQ:new/delete调用构造函数/析构函数; new是类型安全的,malloc不是; new可以被类覆盖.

FQA:FAQ提到的新的优点不是美德,因为构造函数,析构函数和运算符重载都是垃圾(看看没有垃圾收集会发生什么?),类型安全问题在这里很小(通常你有)将malloc返回的void*转换为右指针类型,将其分配给一个类型化的指针变量,这可能很烦人,但远非"不安全").

哦,使用值得信赖的旧malloc可以使用同样值得信赖的旧realloc.太糟糕了,我们没有一个闪亮的新操作员更新或什么.

尽管如此,即使语言是C++,新的也不足以证明偏离整个语言使用的共同风格是正确的.特别是,如果只是对象进行malloc操作,那么具有非平凡构造函数的类将会以致命的方式行为异常.那么为什么不在整个代码中使用新的呢?人们很少会将操作员重新设置为新的,所以它可能不会过多地妨碍你.如果他们确实过载新的,你总是可以让他们停下来.

对不起,我无法抗拒.:)

  • 那是一场骚乱*!谢谢. (7认同)
  • 我不能认真对待这个评论,因为它清楚地表明了作者对C++的偏见.C++是一种用于创建面向性能的软件的语言,而垃圾收集器只能对其目标产生不利影响.我不同意你的全部答案! (7认同)
  • @Miguel 你错过了这个笑话。 (2认同)

Fer*_*cio 49

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

void *p = operator new(size);
   ...
operator delete(p);
Run Code Online (Sandbox Code Playgroud)

  • 如果直接调用operator new,则需要将字节数作为参数进行分配. (11认同)
  • `operator new`的反义词是`operator delete`.在类型为`void*`的表达式上调用`delete`并不是一个定义明确的操作. (9认同)
  • 有趣的是,当我需要像这样的原始数据缓冲区时,我总是只分配一个unsigned char数组. (3认同)
  • Hrm 不确定,我从未听说过这种语法。 (2认同)

dmc*_*kee 31

使用malloc用于分配要由C-中心库和API来管理内存.对您控制的所有内容使用和(以及变体).free newdelete[]

  • 另请注意,编写良好的C库将隐藏malloc并在内部自由,这就是C程序员应该如何工作的. (9认同)
  • @Dacav:如果一个 C 函数将接受一个指向它在函数返回后需要继续使用的对象的指针,而调用者将无法知道何时仍然需要该对象,那么该函数将是完全合理的指定指针必须是用 `malloc` 创建的。同样,如果像 `strdup` 这样的函数需要创建一个对象并将其返回给调用者,那么指定调用者必须在不再需要该对象时调用该对象上的 `free` 是完全合理的。这些函数如何避免将它们对 malloc/free 的使用暴露给调用者? (2认同)

Yog*_*H T 26

new vs malloc()

1)new是一个操作员,同时malloc()也是一个功能.

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

3)new返回确切的数据类型,同时malloc()返回void*.

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

5)的存储器再分配不通过处理newmalloc()可以

  • 嗨,对于第4点),可以指示new在失败时返回NULL.`char*ptr = new(std :: nothrow)char [323232];` (6认同)
  • 6) new 从构造函数参数创建,而 malloc 使用大小。 (2认同)

The*_*ist 16

要回答你的问题,你应该知道的区别mallocnew.区别很简单:

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

规则的一些例外情况:

  • 您正在编写一个具有高级数据结构的标准库,其中 malloc 是合适的
  • 您必须分配大量内存(10GB 文件的内存副本?)
  • 你有工具阻止你使用某些结构
  • 您需要存储不完整的类型


her*_*tao 6

有一些事情new没有malloc做到:

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

所以,如果你使用malloc,那么你需要明确地做上面的事情,这并不总是实际的.另外,new可以重载但malloc不能.


小智 6

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


PSk*_*cik 5

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

无论如何,我不明白为什么不应该同时使用两种方法(前提是您释放了已分配的内存并删除了用new分配的对象),如果可以利用速度提升的优势(如果您要重新分配大型数组,有时会很重要)重新分配可以给您的POD)。

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