我看到在C ++中有多种分配和释放数据的方法,而且我了解,当您调用malloc
时应调用,free
并且在使用new
运算符时应与之配对,delete
将两者混合使用是错误的(例如,调用free()
已创建的内容)与new
操作员联系),但我不清楚在实际程序中何时应使用malloc
/ free
以及何时应使用new
/ delete
。
如果您是C ++专家,请让我知道您在这方面遵循的经验法则或惯例。
我看到在C ++中有多种分配和释放数据的方法,而且我了解,当您调用malloc
时应调用,free
并且在使用new
运算符时应与之配对,delete
将两者混合使用是错误的(例如,调用free()
已创建的内容)与new
操作员联系),但我不清楚在实际程序中何时应使用malloc
/ free
以及何时应使用new
/ delete
。
如果您是C ++专家,请让我知道您在这方面遵循的经验法则或惯例。
Answers:
除非您被迫使用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
new[]
比这更安全std::vector
吗?如果使用new[]
,则指针变为无效的唯一方法是通过explicit delete
,而std::vector
当向量调整大小或离开作用域时,为分配的内存可能无效。(请注意,使用async时,必须考虑async方法仍处于未决状态new[]
而无法调用的可能性delete
;如果可能有必要放弃async操作,则可能必须安排通过回调进行删除) 。
简短的答案是:不要在没有malloc
充分理由的情况下使用C ++。malloc
与C ++一起使用时,存在许多缺陷,new
可以克服这些缺陷。
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
}
比这更糟。如果有问题的类型是POD(普通旧数据),那么您可以malloc
像f2
第一个示例中那样半明智地使用它来为其分配内存。
如果类型是POD,则不是很明显。一个重要的因素是,给定类型可能从POD更改为非POD,而不会导致编译器错误,并且可能很难调试问题。例如,如果某个人(可能是另一个程序员,在维护过程中,后来又进行了更改而导致foo
不再是POD),那么在编译时就不会出现您希望的明显错误,例如:
struct foo {
double d[5];
virtual ~foo() { }
};
没有任何明显的诊断方法malloc
,f2
也会使of 变坏。这里的示例很简单,但是有可能在更远的地方意外地引入了非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)));
}
malloc
NULL
如果分配失败,则返回。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;
}
从根本上讲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();
}
我们的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
!
struct
,它的class
意思基本相同。我想知道struct
为POD保留是否会出现任何问题,并且可能将所有class
类型都假定为非POD。在C ++发明之前,由代码定义的任何类型都必然是POD,因此我不认为向后兼容性会成为一个问题。将非POD类型声明为struct
而不有优势class
?
$class
。我不知道有做什么class
和struct
不过是同义词。
class
与struct
平均有效同样的事情,你可以做他们(任意变换$class
)而不进行堪忧class
一个struct
,反之亦然。
[16.4]为什么我应该使用new而不是值得信赖的旧malloc()?
常见问题解答:new / delete调用构造函数/析构函数;new是类型安全的,malloc不是;new可以被类覆盖。
FQA:FAQ中提到的new的优点不是优点,因为构造函数,析构函数和运算符重载是垃圾(请参阅没有垃圾回收时会发生什么?),并且类型安全问题在这里确实很小(通常您有将malloc返回的void *强制转换为正确的指针类型,以将其分配给类型化的指针变量,这可能很烦人,但远非“不安全”)。
哦,使用值得信赖的旧版malloc可以使用同样值得信赖的旧版realloc。不幸的是,我们没有新的运营商续订或其他新的东西。
尽管如此,即使语言是C ++,new仍不足以证明与某种语言所使用的通用样式有所偏离。特别是,如果简单地分配对象,则具有非平凡构造函数的类将以致命的方式行为异常。那么,为什么不在整个代码中使用new?人们很少会重载运算符new,因此它可能不会过多地妨碍您。如果它们确实超载了,您可以随时要求它们停止。
抱歉,我无法抗拒。:)
始终在C ++中使用new。如果需要一块无类型的内存,则可以直接使用运算符new:
void *p = operator new(size);
...
operator delete(p);
operator new
是operator delete
。调用delete
类型为type的表达式不是明确定义的操作void*
。
使用malloc
并只用于分配要由C-中心库和API来管理内存。对您控制的所有内容使用和(及其变体)。free
new
delete
[]
malloc
。同样,如果像这样的函数strdup
需要创建一个对象并将其返回给调用者,则完全合理的做法是指定free
当不再需要该对象时,调用者必须在该对象上进行调用。这样的函数如何避免将其对malloc / free的使用暴露给调用者?
malloc
在C ++?
新vs malloc()
1)new
是运算符,malloc()
而是函数。
2)new
调用构造函数,而malloc()
不会。
3)new
返回确切的数据类型,而malloc()
返回void *。
4)new
永不返回NULL(将抛出失败),而malloc()
返回NULL
5)不能由new
while malloc()
可以处理的内存的重新分配
char* ptr = new (std::nothrow) char [323232];
new
功能
realloc
而不是malloc
,并从初始化为的指针变量开始NULL
。另一方面,如果您想要在C ++中可调整大小的内存块,我建议std::vector
不要使用realloc
...那或一个文件。
malloc
和之间有一个很大的区别new
。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
仅当对象的生存期与创建对象的作用域不同时才需要动态分配(这同样适用于使作用域变小或变大),并且您有特定的原因不按值存储它工作。
例如:
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
即使上述内容有效,也可以通过新操作符来完成。std::vector
这是一个很好的例子。
最后,我们仍然在房间里放大象: C
。如果您必须使用C库来在C ++代码中分配内存并在C代码中释放内存(或者相反),则必须使用malloc / free。
如果是这种情况,请忽略虚拟函数,成员函数,类...仅允许其中包含POD的结构。
规则的一些例外:
有这几件事情new
确实是malloc
不:
new
通过调用该对象的构造函数来构造该对象new
不需要类型转换分配的内存。因此,如果使用malloc
,则需要显式地执行上述操作,这并不总是可行的。此外,new
可以超载,但malloc
不能超载。
如果您使用不需要构造/销毁并且需要重新分配的数据(例如,大量的int),那么我相信malloc / free是一个不错的选择,因为它可以为您提供重新分配,这比new-memcpy更快-delete(在我的Linux机器上,但是我想这可能取决于平台)。如果使用不是POD且需要构造/销毁的C ++对象,则必须使用new和delete运算符。
无论如何,我不明白为什么不应该同时使用两种方法(前提是您释放了已分配的内存并删除了用new分配的对象),如果可以利用速度提升的优势(如果您要重新分配大型数组,有时会很重要)重新分配可以给您的POD)。
除非您有需要,否则应该坚持使用C ++中的new / delete。
如果您有C代码,想移植到C ++,则可以在其中保留所有malloc()调用。对于任何新的C ++代码,我建议改用new。
从较低的角度看,new将在提供内存之前初始化所有内存,而malloc将保留内存的原始内容。
malloc()用于动态分配C中的内存,而c ++中的new()完成相同的工作。因此,您不能混合使用两种语言的编码约定。如果您要求calloc和malloc()之间的区别会很好
malloc
在C ++中使用。