558

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

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

4

20 回答 20

455

除非你被迫使用 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/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
于 2008-10-08T19:48:43.073 回答
161

简短的回答是:不要在没有malloc充分理由的情况下使用 C++。malloc与 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
    }
    
  2. 虽然比这更糟。如果有问题的类型是POD(普通旧数据) ,那么您可以像第一个示例中那样,半明智地使用malloc它来为其分配内存。f2

    虽然类型是 POD,但它并不那么明显。给定类型有可能从 POD 更改为非 POD,而不会导致编译器错误并且可能很难调试问题,这一事实是一个重要因素。例如,如果某人(可能是另一个程序员,在维护期间,很久以后要进行导致foo不再是 POD 的更改,那么在编译时不会像您希望的那样出现明显的错误,例如:

    struct foo {
      double d[5];
      virtual ~foo() { }
    };
    

    在没有任何明显诊断的情况下,也会使mallocof变坏。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。

  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;
    }
    
  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();
    }
    
  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)...);
      }
    };
    

    现在,尽管在解决我们迄今为止发现的所有问题时,我们实际上已经重新发明了默认new运算符。如果您要使用malloc和放置,new那么您还不如从使用new开始!

于 2011-11-01T17:04:54.523 回答
57

来自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,所以它可能不会妨碍你太多。如果他们确实超载了新的,你可以随时要求他们停下来。

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

于 2008-10-08T20:24:37.527 回答
51

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

void *p = operator new(size);
   ...
operator delete(p);
于 2008-10-08T19:54:11.533 回答
45

新 vs malloc()

1)new运算符,whilemalloc()函数

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

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

4)在返回 NULL时new从不返回NULL(将抛出失败)malloc()

new5) whilemalloc()可以重新分配未处理的内存

于 2015-11-26T10:06:05.297 回答
33

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

于 2008-10-08T19:51:13.320 回答
32

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

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

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

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

于 2017-07-29T07:20:05.430 回答
15

malloc和之间有一个很大的区别newmalloc分配内存。这对 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
于 2011-11-01T18:25:37.973 回答
11

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

例如:

 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 文件的内存副本中?)
  • 您有工具阻止您使用某些构造
  • 您需要存储不完整的类型
于 2018-12-22T18:08:38.900 回答
6

有一些事情new不会malloc

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

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

于 2014-01-15T14:45:51.287 回答
5

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

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

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

于 2013-04-09T12:23:20.363 回答
3

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

于 2014-04-02T09:13:02.607 回答
2

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

于 2008-10-08T19:52:10.087 回答
2

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

例如

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

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

于 2016-12-14T15:46:27.767 回答
1

在下面的场景中,我们不能使用 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;
    }
};
于 2012-08-17T00:54:23.110 回答
1

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

于 2011-08-14T20:31:35.713 回答
0

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

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

于 2008-10-08T20:42:26.447 回答
0

顺便说一句,如果你有 malloc 的代码,请使用这个正则表达式:

搜索:

\((\w+)\*\)malloc\((\w+)\)

用。。。来代替:

new \1[\2]
于 2022-01-06T22:00:22.693 回答
0

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

于 2017-07-29T06:17:05.673 回答
-4

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

于 2012-07-26T05:41:38.067 回答