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

我看到在c++中有多种方法来分配和释放数据,我明白,当你叫# EYZ0你应该叫# EYZ1当你使用# EYZ2运算符应该搭配# EYZ3和它是错误的把两个(例如调用# EYZ4在创建一些# EYZ2操作符),但是我不清楚我应该使用# EYZ0 / # EYZ1当我应该在现实世界中使用# EYZ2 / # EYZ3项目。

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

335908 次浏览

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

使用mallocfree 只有来分配内存,这些内存将由以c为中心的库和api管理。使用newdelete(和free0变体)你控制的一切。

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

在c++中总是使用new。如果你需要一个无类型的内存块,你可以直接使用operator new:

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

c++ FQA Lite:

[16.4]为什么我要用new代替 值得信赖的老malloc()?< / p >

FAQ: new/delete call the 构造/析构函数;新是类型 安全有,malloc不在;新可以是

FQA:新提到的美德 FAQ不是美德,因为 构造函数、析构函数和 操作符重载是垃圾(参见 如果你没有垃圾会发生什么 集合?),以及类型安全 这里的问题非常小(通常情况下) 你必须施放虚空*返回 Malloc到右指针类型到 将它赋值给一个类型化指针变量, 这可能很烦人,但远非如此 “不安全”)。< / p >

哦,还有使用值得信赖的旧malloc 这样就可以平等地使用 值得信赖的,老realloc。太糟糕了,我们 不要有闪亮的新操作符renew之类的

不过,新的还不够坏 证明背离常规是合理的 甚至在一种语言中使用的样式 当语言是c++时。在 特别是具有非平凡类的类 构造函数会在fatal中出错 方法,如果你只是malloc对象。 所以为什么不在整个过程中使用new呢 代码?人很少超载操作员 新的,所以它可能进不了你的 太多了。如果它们超载了 新的,你可以让他们停止。

对不起,我就是忍不住。:)

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

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

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

简短的回答是:如果没有很好的理由,不要在c++中使用mallocmalloc在与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. It's worse than that though. If the type in question is POD (plain old data) then you can semi-sensibly use malloc to allocate memory for it, as f2 does in the first example.

    It's not so obvious though if a type is POD. The fact that it's possible for a given type to change from POD to non-POD with no resulting compiler error and potentially very hard to debug problems is a significant factor. For example if someone (possibly another programmer, during maintenance, much later on were to make a change that caused foo to no longer be POD then no obvious error would appear at compile time as you'd hope, e.g.:

    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是无法确定该类型是否为POD,没有c++ 11或其他编译器扩展

  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. Fundamentally malloc is a C feature and new is a C++ feature. As a result malloc does not play nicely with constructors, it only looks at allocating a chunk of bytes. We could extend our safe_foo_malloc further to use placement 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. Our safe_foo_malloc function isn't very generic - ideally we'd want something that can handle any type, not just foo. We can achieve this with templates and variadic templates for non-default constructors:

    #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开始!< / p >

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对象。请参阅另一个答案了解更多信息。

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

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

由于这些原因,在c++代码中应该使用new,而不是malloc。但即使这样,你也不应该“公开地”使用new,因为它获取了你以后需要释放的资源。当你使用new时,你应该立即将它的结果传递给一个资源管理类:

std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak

malloc()用于在C中动态分配内存 而在c++中,同样的工作由new()完成。 所以你不能混合两种语言的编码习惯。 如果你要求calloc和malloc()之间的差异

,那就好了

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

如果你处理的数据不需要构造/销毁,需要重新分配(例如,一个大的整数数组),那么我相信malloc/free是一个很好的选择,因为它给你realloc,这比new-memcpy-delete快得多(它在我的Linux盒子上,但我猜这可能是平台相关的)。如果你使用的c++对象不是POD并且需要构造/销毁,那么你必须使用new和delete操作符。

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

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

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

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

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

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

New vs malloc()

1) new操作符,而malloc()函数

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

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

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

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

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

如。

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

因此,new struct test_s将返回一个带有工作引用的初始化结构,而malloc'ed版本没有默认值,实习引用也没有初始化。

很少考虑使用malloc/free而不是new/delete的情况是,当你使用realloc进行分配和重新分配(简单的pod类型,而不是对象)时,因为在c++中没有类似于realloc的函数(尽管这可以使用更c++的方法来完成)。

要回答你的问题,你应该知道#EYZ0和new之间的差异。区别很简单:

malloc 分配内存,而new 分配内存并调用构造函数你正在分配内存的对象。

因此,除非仅限于使用C,否则永远不要使用malloc,特别是在处理c++对象时。这将会破坏你的程序。

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

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

例如:

 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{};

一旦'instance'超出作用域,内存就会被清理。转让所有权也很容易:

 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

确保正确跟踪所有权,以免出现内存泄漏!Move语义也不能工作。

那么,在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操作符来完成。std::vector就是一个很好的例子。

最后,我们仍然有一个房间里的大象:C。如果你必须使用在c++代码中分配内存并在C代码中释放内存的C库(或者相反),你就不得不使用malloc/free。

如果你是在这种情况下,忘记虚函数、成员函数、类……只有包含pod的结构才被允许。

规则的一些例外情况:

  • 您正在编写一个具有高级数据结构的标准库,其中malloc是合适的
  • 你必须分配大量的内存(在10GB文件的内存副本中?)
  • 您拥有阻止您使用某些构造的工具
  • 您需要存储不完整的类型
我以前玩过一些计算机图形的C/ c++应用程序。 过了这么久,有些东西已经消失了,我很想念它们 关键是,malloc和new,或free和delete,都可以工作, 特别是对于某些基本类型,这是最常见的

例如,一个char数组,可以用malloc或new来分配。 一个主要的区别是,使用new,你可以实例化一个固定的数组大小

char* pWord = new char[5]; // allocation of char array of fixed size

在这种情况下,数组的大小不能使用变量。 相反,malloc函数可以允许变量大小

int size = 5;
char* pWord = (char*)malloc(size);
在这种情况下,可能需要一个转换强制转换操作符。 对于malloc返回的类型,它是指向void的指针,而不是char。 有时候编译器不知道如何转换这个类型

分配内存块后,可以设置变量值。 对于一些较大的数组,memset函数确实会慢一些。 但是在赋值之前,所有的bit必须先设置为0。 因为数组的值可以有任意的内容

假设,数组被分配给另一个更小的数组。 数组元素的一部分仍然可以有任意内容。 在这种情况下,建议调用memset函数

memset((void*)pWord, 0, sizeof(pWord) / sizeof(char));
分配函数可用于所有C包。 这些是通用函数,必须适用于更多的C类型。 c++库是旧C库的扩展。 因此malloc函数返回一个泛型void*指针。 该结构没有定义new或delete操作符。 在这种情况下,可以使用malloc分配自定义变量 new和delete关键字实际上是一些定义好的C操作符。 也许一个自定义联合或类可以定义这些操作符。 如果在类中没有定义new和delete,则它们可能不起作用。 但如果一个类是由另一个类派生的,这个类有这些操作符, new和delete关键字可以具有基本的类行为

关于释放数组,free只能与malloc配合使用。 不能用malloc分配变量,然后用delete释放 简单的删除操作符只引用数组的第一项。 因为pWord数组也可以写成:

pWord = &pWord[0]; // or *pWord = pWord[0];

当必须删除一个数组时,使用delete[]操作符:

delete[] pWord;
类型转换并不坏,只是不适用于所有变量类型。 转换类型转换也是一个必须定义的运算符函数。 如果没有为某个类型定义此操作符,则它可能不起作用。 但并不是所有的错误都是因为这个转换强制转换操作符 另外,当使用free调用时,必须使用到void指针的类型转换。 这是因为free函数的实参是一个空指针
free((void*)pWord);

可能会出现一些错误,因为数组的大小太小。 但这是另一个故事,这并不是因为使用了演员

致以亲切的问候,艾德里安·布里纳斯