我在C ++中看到有多种方式可以分配和释放数据,我知道当您致电malloc
时,您应该致电free
,当您使用new
运算符时,您应该配对使用delete
将两者混合是错误的(例如,在使用free()
运算符创建的内容上调用new
),但我不知道何时应该使用{ {1}} / malloc
以及我应该在我的真实世界计划中使用free
/ new
。
如果您是C ++专家,请告诉我您在这方面遵循的任何经验法则或惯例。
答案 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
/ 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
答案 1 :(得分:132)
简短的回答是:如果没有充分的理由,请不要将malloc
用于C ++。与{C ++一起使用时malloc
有许多不足之处,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 (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。
#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;
}
是C功能,malloc
是C ++功能。结果new
与构造函数不能很好地协作,它只关注分配一块字节。我们可以进一步扩展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
!
答案 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)
要回答您的问题,您应该知道 malloc
和new
之间的差异。区别很简单:
malloc
分配内存,而new
分配内存并调用您为其分配内存的对象的构造函数。 / p>
因此,除非您被限制为C,否则不应使用malloc,尤其是在处理C ++对象时。这将是打破你的计划的一个秘诀。
free
和delete
之间的区别也大致相同。区别在于delete
除了释放内存外,还会调用对象的析构函数。
答案 7 :(得分:9)
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对象。有关详细信息,请参阅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
做的一些事情:
new
通过调用该对象的构造函数来构造对象malloc
不需要对已分配的内存进行类型转换。所以,如果你使用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的结构。
规则的某些例外情况:
答案 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)
new
和delete
运算符可以对类和结构进行操作,而malloc
和free
仅适用于需要强制转换的内存块。
使用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()
之间的区别,那将是件好事