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

我看到在C ++中有多种分配和释放数据的方法,并且我了解,当您调用malloc您应该调用free而当您使用new运算符时,您应该与delete配对,并且将两者混用是错误的(例如,调用free()在使用new运算符创建的内容上),但是我不清楚在实际程序中何时应使用malloc / free以及何时应使用new / delete

如果您是C ++专家,请让我知道您在这方面遵循的经验法则或惯例。


#1楼

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


#2楼

在以下情况下,我们不能使用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;
    }
};

#3楼

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

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

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


#4楼

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

#5楼

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


#6楼

如果您有C代码,想移植到C ++,则可以在其中保留所有malloc()调用。 对于任何新的C ++代码,我建议改用new。


#7楼

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

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

#8楼

C ++ FQA Lite

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

常见问题解答:new / delete调用构造函数/析构函数; new是类型安全的,malloc不是; new可以被类覆盖。

FQA:FAQ中提到的new的优点不是优点,因为构造函数,析构函数和运算符重载是垃圾(请参阅没有垃圾回收时会发生什么?),并且类型安全问题在这里确实很小(通常您有强制将malloc返回的void *转换为正确的指针类型,以将其分配给类型化的指针变量,这可能很烦人,但远非“不安全”)。

哦,使用值得信赖的旧版malloc可以使用同样值得信赖的旧版realloc。 不幸的是,我们没有新的运营商续订或其他新的东西。

尽管如此,即使语言是C ++,new仍不足以证明偏离一种语言所使用的通用样式。 特别是,如果您简单地分配对象,则具有非平凡构造函数的类将以致命的方式行为异常。 那么,为什么不在整个代码中使用new? 人们很少重载运算符new,因此它可能不会过多地妨碍您。 如果它们确实超载了,您可以随时要求它们停止。

抱歉,我无法抗拒。 :)


#9楼

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

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


#10楼

malloc并没有提供一些new功能:

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

因此,如果使用malloc ,则需要显式地执行上述操作,这并不总是可行的。 另外, new可以重载,但malloc不能。


#11楼

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


#12楼

新vs malloc()

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

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

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

4) new从不返回NULL (将引发失败),而malloc()返回NULL

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


#13楼

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

例如

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

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


#14楼

很少考虑使用malloc / free而不是new / delete是在使用realloc进行分配然后重新分配(简单的pod类型,不是对象)时,因为在C ++中没有类似的函数可以重新分配(尽管可以使用a更多的C ++方法)。


#15楼

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

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

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

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


#16楼

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

例如:

 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文件的内存副本中?)
  • 您的工具阻止了您使用某些构造
  • 您需要存储不完整的类型

#17楼

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


#18楼

简短的答案是:在没有充分理由的情况下,请勿将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(普通旧数据),则可以像第一个示例中的f2一样,半明智地使用malloc为它分配内存。

    如果类型是POD,则不是很明显。 一个重要的因素是给定类型可能从POD更改为非POD,而不会导致编译器错误,并且可能很难调试问题。 例如,如果某人(可能是另一个程序员,在维护期间,后来又进行了更改,导致foo不再是POD),那么在编译时就不会出现您希望的明显错误,例如:

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

    会使f2malloc也变坏,而没有任何明显的诊断。 这里的示例很简单,但是有可能在更远的地方意外地引入了非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. 如果分配失败,则malloc返回NULLnew将抛出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在构造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运算符。 如果要使用mallocnew放置,那么最好还是使用new作为开始!


#19楼

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对象。 有关更多信息,请参见此其他答案

另一个区别是失败时的行为。 当分配内存失败时, malloc返回一个空指针,而new抛出异常。

前者要求您在使用它之前测试返回的每个指针,而后者则将始终产生有效的指针。

由于这些原因,在C ++代码中,您应该使用new而不是malloc 。 但是即使那样,您也不应使用new “公开”方式,因为它获取了以后需要释放的资源。 使用new ,应立即将其结果传递到资源管理类中:

std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
发布了0 篇原创文章 · 获赞 2 · 访问量 7504

猜你喜欢

转载自blog.csdn.net/asdfgh0077/article/details/104091656
今日推荐