我在 C++ 中看到有多种分配和释放数据的方法,我知道当您调用 malloc
时,您应该调用 free
,而当您使用 new
运算符时,您应该与 delete
配对,这是一个错误将两者混合(例如,在使用 new
运算符创建的东西上调用 free()
),但我不清楚何时应该使用 malloc
/ free
以及何时应该使用 new
/ delete
在我的真实世界程序中。
如果您是 C++ 专家,请告诉我您在这方面遵循的任何经验法则或约定。
除非您被迫使用 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
简短的回答是:不要在没有充分理由的情况下将 malloc
用于 C++。 malloc
与 C++ 一起使用时存在许多缺陷,new
旨在克服这些缺陷。
新的 C++ 代码修复的缺陷
malloc 在任何意义上都不是类型安全的。在 C++ 中,您需要从 void* 转换返回值。这可能会引入很多问题:#include
struct
和 class
的意思基本相同;我想知道将 struct
保留给 POD 并可能将所有 class
类型假定为非 POD 是否会有任何问题。由 C++ 发明之前的代码定义的任何类型都必然是 POD,因此我认为向后兼容性不会成为问题。将非 POD 类型声明为 struct
而不是 class
是否有优势?
$class
)的语言版本。但是,我不确定这与 class
和 struct
是同义词有什么关系。
class
和 struct
实际上表示相同的东西,您可以对它们进行任意转换 ($class
),而不必担心将 class
变成 struct
,反之亦然。
从 C++ FQA Lite:
[16.4] 为什么我应该使用new而不是值得信赖的旧malloc()? FAQ:new/delete调用构造函数/析构函数; new 是类型安全的, malloc 不是; new 可以被一个类覆盖。 FQA:FAQ 中提到的 new 的优点不是优点,因为构造函数、析构函数和操作符重载都是垃圾(看看没有垃圾收集会发生什么?),类型安全问题在这里真的很小(通常你有将 malloc 返回的 void* 转换为正确的指针类型,以将其分配给类型化的指针变量,这可能很烦人,但远非“不安全”)。哦,使用可信赖的旧 malloc 可以使用同样可信赖的旧 realloc。太糟糕了,我们没有闪亮的新运营商更新或其他东西。尽管如此,new 还不足以证明偏离整个语言使用的通用风格是合理的,即使该语言是 C++ 也是如此。特别是,如果您只是 malloc 对象,则具有非平凡构造函数的类将以致命的方式行为不端。那么为什么不在整个代码中使用 new 呢?人们很少重载 operator new,所以它可能不会妨碍你太多。如果他们确实超载了新的,你可以随时要求他们停下来。
对不起,我无法抗拒。 :)
始终在 C++ 中使用 new。如果你需要一块无类型的内存,你可以直接使用 operator new:
void *p = operator new(size);
...
operator delete(p);
operator new
的反义词是 operator delete
。对类型为 void*
的表达式调用 delete
不是一个定义明确的操作。
新 vs malloc()
1) new
是运算符,而 malloc()
是函数。
2) new
调用 构造函数,而 malloc()
不调用。
3) new
返回精确数据类型,而 malloc()
返回void *。
4) new
从不返回 NULL(将抛出失败),而 malloc()
返回 NULL
5) new
未处理的内存重新分配,而 malloc()
可以
char* ptr = new (std::nothrow) char [323232];
new
函数
realloc
而不是 malloc
,并从初始化为 NULL
的指针变量开始。另一方面,如果您想要 C++ 中的 resizable 内存块,我建议使用 std::vector
而不是 realloc
... 那或文件。
malloc
和 free
only 用于分配将由以 c 为中心的库和 API 管理的内存。对您控制的所有内容使用 new
和 delete
(以及 []
变体)。
malloc
创建指针。同样,如果像 strdup
这样的函数需要创建一个对象并将其返回给调用者,那么指定调用者必须在不再需要对象时调用 free
是完全合理的。这样的函数如何避免将它们对 malloc/free 的使用暴露给调用者?
char c;
在 C 和 C++ 中,此变量表示一个对象。不同之处在于C++ 中的一些(但不是全部)对象是也是 多态的(毕竟C++ 是面向对象的)。不要误以为只有面向对象的代码才能使用对象。
要回答您的问题,您应该知道malloc
和 new
之间的区别。区别很简单:
malloc
分配内存,而new
分配内存并调用您为其分配内存的对象的构造函数。
因此,除非您仅限于使用 C,否则您永远不应该使用 malloc,尤其是在处理 C++ 对象时。那将是破坏您的程序的秘诀。
free
和 delete
之间的区别也完全相同。不同之处在于 delete
除了释放内存外,还会调用对象的析构函数。
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
如果你真的想要它,你可以使用 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
仅当对象的生命周期应与其创建的范围不同时才需要动态分配(这也适用于使范围变小或变大)并且您有特定原因不按值存储它工作。
例如:
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{};
一旦“实例”超出范围,内存就会被清理。转让所有权也很容易:
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 呢?唯一有效的原因是分配内存并稍后通过placement 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 文件的内存副本中?)
您有工具阻止您使用某些构造
您需要存储不完整的类型
malloc
或 new
调用。
new
做了一些 malloc
没有做的事情:
new 通过调用该对象的构造函数来构造对象 new 不需要对分配的内存进行类型转换。它不需要分配大量内存,而是需要构造多个对象。
因此,如果您使用 malloc
,那么您需要明确地执行上述操作,这并不总是实用的。此外,new
可以重载,但 malloc
不能。
如果您使用不需要构造/销毁且需要重新分配的数据(例如,大量整数数组),那么我相信 malloc/free 是一个不错的选择,因为它为您提供了 realloc,这比 new-memcpy 快得多-delete(它在我的 Linux 机器上,但我想这可能取决于平台)。如果您使用非 POD 且需要构造/销毁的 C++ 对象,则必须使用 new 和 delete 运算符。
无论如何,我不明白为什么你不应该同时使用两者(前提是你释放你分配的内存并删除分配给新的对象)如果可以利用速度提升(如果你正在重新分配大型数组,有时是一个重要的提升)的POD)realloc可以给你。
除非你需要它,否则你应该坚持使用 C++ 中的 new/delete。
如果您使用 C++,请尝试使用 new/delete 而不是 malloc/calloc,因为它们是运算符。对于 malloc/calloc,您需要包含另一个标头。不要在同一代码中混合使用两种不同的语言。他们的工作在各个方面都相似,都从哈希表中的堆段动态分配内存。
如果您有想要移植到 C++ 的 C 代码,您可能会在其中留下任何 malloc() 调用。对于任何新的 C++ 代码,我建议使用 new 代替。
new
将初始化结构的默认值并将其中的引用正确链接到自身。
例如
struct test_s {
int some_strange_name = 1;
int &easy = some_strange_name;
}
所以 new struct test_s
将返回一个带有工作引用的初始化结构,而 malloc 的版本没有默认值,并且内部引用没有初始化。
从较低的角度来看,new 将在给予内存之前初始化所有内存,而 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 而不是 new/delete 的罕见情况是,当您使用 realloc 进行分配然后重新分配(简单的 pod 类型,而不是对象)时,因为在 C++ 中没有与 realloc 类似的功能(尽管可以使用更多 C++ 方法)。
new
和 delete
运算符可以对类和结构进行操作,而 malloc
和 free
仅适用于需要强制转换的内存块。
使用 new/delete
将有助于改进您的代码,因为您不需要将分配的内存转换为所需的数据结构。
malloc() 用于在 C 中动态分配内存,而在 c++ 中由 new() 完成相同的工作。因此,您不能混合使用 2 种语言的编码约定。如果你问 calloc 和 malloc() 之间的区别会很好
malloc
。
new[]
不会比std::vector
更安全吗?如果使用new[]
,则指针变为无效的唯一方法是通过显式delete
,而为std::vector
分配的内存可能会在向量调整大小或离开范围时失效。 (请注意,当使用new[]
时,如果异步方法仍处于挂起状态,则必须考虑可能无法调用delete
的可能性;如果可能需要放弃异步操作,则可能必须通过回调安排删除)。