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

时间:2008-10-08 19:47:55

标签: c++ memory-management malloc new-operator

我在C ++中看到有多种方式可以分配和释放数据,我知道当您致电malloc时,您应该致电free,当您使用new运算符时,您应该配对使用delete将两者混合是错误的(例如,在使用free()运算符创建的内容上调用new),但我不知道何时应该使用{ {1}} / malloc以及我应该在我的真实世界计划中使用free / new

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

19 个答案:

答案 0 :(得分:342)

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

答案 1 :(得分:132)

简短的回答是:如果没有充分的理由,请不要将malloc用于C ++。与{C ++一起使用时malloc有许多不足之处,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 (plain old data),那么您可以半合理地使用malloc为其分配内存,如第一个示例中f2所做的那样。

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

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

    会使malloc的{​​{1}}变得不好,没有任何明显的诊断。这里的例子是微不足道的,但是可能会意外地将非PODness引入更远的地方(例如在基类中,通过添加非POD成员)。如果您有C ++ 11 / boost,可以使用f2检查此假设是否正确,如果不是,则会产生错误:

    is_pod

    虽然在没有C ++ 11或其他编译器扩展的情况下boost是unable to determine if a type is POD

  3. 如果分配失败,
  4. #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))); } 会返回mallocNULL会抛出new。稍后使用std::bad_alloc指针的行为未定义。抛出异常时会出现干净的语义,并且会从错误源中抛出异常。在每次通话时对NULL进行适当的测试包装似乎很乏味且容易出错。 (你只需要忘记一次撤消所有好的工作)。可以允许异常传播到调用者能够明智地处理它的级别,其中malloc更难以有意义地传回。我们可以扩展我们的NULL函数来抛出异常或退出程序或调用一些处理程序:

    safe_foo_malloc
  5. 基本上#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功能,malloc是C ++功能。结果new与构造函数不能很好地协作,它只关注分配一块字节。我们可以进一步扩展malloc以使用展示位置safe_foo_malloc

    new
  6. 我们的#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

答案 2 :(得分:50)

来自C++ FQA Lite

  

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

     

常见问题:新/删除电话   构造函数/析构函数;新类型   安全,malloc不是;新的可以   被班级覆盖。

     

FQA:新提到的美德   常见问题解答不是美德,因为   构造函数,析构函数和   运算符重载是垃圾(参见   当你没有垃圾时会发生什么   集合?)和类型安全   问题在这里很小(通常   你必须施放由*返回的void *   malloc到右边的指针类型   将它分配给类型化的指针变量,   这可能很烦人,但远非如此   “不安全”)。

     

哦,并使用值得信赖的旧malloc   使得同样可以使用   值得信赖的旧的realloc。我们太糟糕了   没有闪亮的新操作员续订或其他什么。

     

尽管如此,新的还不够糟糕   证明偏离共同的理由   整个语言使用的风格,甚至   当语言是C ++时。在   特别是,非平凡的课程   施工人员会致命地行为不端   如果你只是malloc对象的方式。   那么为什么不在整个过程中使用new   码?人们很少超载运营商   新的,所以它可能不会进入你的   方式太多了。如果他们超载   新的,你可以随时要求他们停下来。

抱歉,我无法抗拒。 :)

答案 3 :(得分:48)

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

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

答案 4 :(得分:30)

仅使用 malloc free 来分配将由c-centric管理的内存库和API。对于您控制的所有内容,请使用 new delete (以及 [] 变体)。< / p>

答案 5 :(得分:24)

new vs malloc()

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

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

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

4)new永远不会返回 NULL (将失败),而malloc()返回NULL

5)new malloc()可以

时,nodeList未处理的内存重新分配

答案 6 :(得分:13)

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

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

因此,除非您被限制为C,否则不应使用malloc,尤其是在处理C ++对象时。这将是打破你的计划的一个秘诀。

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

答案 7 :(得分:9)

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

如果您真的需要它,可以使用use 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

答案 8 :(得分:6)

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

  1. new通过调用该对象的构造函数来构造对象
  2. malloc不需要对已分配的内存进行类型转换。
  3. 它不需要分配大量内存,而是需要一定数量的内存 要建造的物体。
  4. 所以,如果你使用new,那么你需要明确地做上面的事情,这并不总是实际的。此外,malloc可能会过载,但{{1}}不能重载。

答案 9 :(得分:4)

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

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

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

答案 10 :(得分:3)

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

答案 11 :(得分:2)

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

答案 12 :(得分:1)

仅当对象的生存期与创建对象的作用域不同时才需要动态分配(这同样适用于使作用域变小或变大),并且您有特定的理由按值存储对象不起作用。

例如:

 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::make_unique<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::make_unique<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?唯一有效的原因是分配内存,然后稍后通过放置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文件的内存副本中?)
  • 您拥有阻止使用某些构造的工具
  • 您需要存储不完整的类型

答案 13 :(得分:1)

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

E.g。

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

因此new struct test_s将返回带有工作引用的初始化结构,而malloc的版本没有默认值,并且实习生引用未初始化。

答案 14 :(得分:1)

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

答案 15 :(得分:0)

考虑使用malloc / free而不是new / delete的罕见情况是你使用realloc分配然后重新分配(简单的pod类型,而不是对象),因为在C ++中没有类似的函数来重新分配(尽管这个可以使用更多C ++方法完成。

答案 16 :(得分:0)

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

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

答案 17 :(得分:-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;
    }
};

答案 18 :(得分:-4)

malloc()用于在C中动态分配内存 而同样的工作是由c ++中的new()完成的。 所以你不能混合使用2种语言的编码约定。 如果你要求calloc和malloc()

之间的区别,那将是件好事