我看到在c++中有多种方法来分配和释放数据,我明白,当你调用malloc时你应该叫自由,当你使用新的操作符应与删除和对它是错误的把两个调用free()(例如,创建新的操作符),但是我不清楚我应该使用malloc / free,当我在现实世界中应该使用新的/删除程序。
如果你是c++专家,请告诉我你在这方面遵循的任何经验法则或惯例。
我看到在c++中有多种方法来分配和释放数据,我明白,当你调用malloc时你应该叫自由,当你使用新的操作符应与删除和对它是错误的把两个调用free()(例如,创建新的操作符),但是我不清楚我应该使用malloc / free,当我在现实世界中应该使用新的/删除程序。
如果你是c++专家,请告诉我你在这方面遵循的任何经验法则或惯例。
当前回答
要回答您的问题,您应该知道malloc和new之间的区别。区别很简单:
malloc分配内存,而new分配内存并调用要分配内存的对象的构造函数。
因此,除非仅限于使用C,否则永远不要使用malloc,特别是在处理c++对象时。这将会破坏你的程序。
free和delete的区别也是一样的。不同之处在于,delete除了释放内存外,还会调用对象的析构函数。
其他回答
有几件事是new做的,malloc没有:
New通过调用该对象的构造函数来构造该对象 New不需要对已分配的内存进行类型转换。 它不需要分配大量的内存,而是需要大量的内存 要构造的对象。
因此,如果使用malloc,则需要显式地执行上述操作,这并不总是实际的。此外,new可以重载,但malloc不能。
除非被迫使用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/free和malloc/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
只有当对象的生命周期与创建它的作用域不同时才需要动态分配(这也适用于使作用域变小或变大),并且您有特定的原因按值存储它不起作用。
例如:
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)
那你什么时候还需要新的?从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操作符来完成。vector就是一个很好的例子。
最后,我们仍然有一个房间里的大象:C。如果你必须使用一个在c++代码中分配内存并在C代码中释放内存的C库(或者相反),你就被迫使用malloc/free。
如果你是在这种情况下,忘记虚函数、成员函数、类……只有包含pod的结构才被允许。
规则的一些例外情况:
您正在编写一个具有高级数据结构的标准库,其中malloc是合适的 你必须分配大量的内存(在10GB文件的内存副本中?) 您拥有阻止您使用某些构造的工具 您需要存储不完整的类型
简短的回答是:如果没有真正好的理由,不要在c++中使用malloc。malloc在与c++一起使用时有许多缺陷,而new定义是为了克服这些缺陷。
c++代码中新修正的缺陷
malloc is not typesafe in any meaningful way. In C++ you are required to cast the return from void*. This potentially introduces a lot of problems: #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 } 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() { } }; would make the malloc of f2 also become bad, without any obvious diagnostics. The example here is trivial, but it's possible to accidentally introduce non-PODness much further away (e.g. in a base class, by adding a non-POD member). If you have C++11/boost you can use is_pod to check that this assumption is correct and produce an error if it's not: #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))); } Although boost is unable to determine if a type is POD without C++11 or some other compiler extensions. malloc returns NULL if allocation fails. new will throw std::bad_alloc. The behaviour of later using a NULL pointer is undefined. An exception has clean semantics when it is thrown and it is thrown from the source of the error. Wrapping malloc with an appropriate test at every call seems tedious and error prone. (You only have to forget once to undo all that good work). An exception can be allowed to propagate to a level where a caller is able to sensibly process it, where as NULL is much harder to pass back meaningfully. We could extend our safe_foo_malloc function to throw an exception or exit the program or call some handler: #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; } 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(); } 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)...); } }; Now though in fixing all the issues we identified so far we've practically reinvented the default new operator. If you're going to use malloc and placement new then you might as well just use new to begin with!
malloc和new有一个很大的区别。Malloc分配内存。这对于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;
这段代码将打印一个未指定的值,因为由malloc创建的POD对象没有初始化。
使用new,可以指定要调用的构造函数,从而获得定义良好的值。
pod_type* p = new pod_type();
std::cout << p->foo; // prints 0
如果你真的想要它,你可以使用use new来获得未初始化的POD对象。有关更多信息,请参阅另一个答案。
另一个区别是失败时的行为。当它分配内存失败时,malloc返回一个空指针,而new则抛出异常。
前者要求您在使用它之前测试返回的每个指针,而后者将始终产生有效的指针。
基于这些原因,在c++代码中应该使用new,而不是malloc。但即便如此,你也不应该“公开”使用新的内容,因为它会占用你以后需要发布的资源。当你使用new时,你应该立即将它的结果传递给一个资源管理类:
std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak