动态分配的使用

Usage of Dynamic Allocation

本文关键字:动态分配      更新时间:2023-10-16

来自Java和C#,我习惯于做以下事情:

byte[] myArray = new byte[10];

而不在乎必须把它清理干净。然而,现在我使用的是C++,您显然必须小心分配和内存泄漏。

我听过一些人说,你应该不惜一切代价避免动态分配,但我也看到一些人"自由地"使用它,当本地堆栈变量足够时,使用new运算符实例化类:

DatabaseConnection conn = new DatabaseConnection("127.0.0.1");
// or
DatabaseConnection conn("127.0.0.1");

我知道在堆上分配的数组要慢得多,但我更喜欢可读性和可扩展性更强的代码,而不是使用动态内存可能会带来的小性能损失。

所以,我的问题是:你真的应该不惜一切代价避免堆分配吗?

我建议在这种情况下,使用

byte myArray[10]; 

如果您需要一个可以复制的数组(例如从函数返回),那么使用vector<byte>是正确的解决方案。

最后的手段应该是使用new,并且如果数据需要在函数外预存,则只分配较小的区域。

不幸的是,书籍并不总是告诉你良好的实践,或者展示了你应该和不应该使用的好例子,例如new——相反,它们展示了像int *arr = new int[5];这样的东西——这可能比实际数据占用更多的开销空间。

当然,所有的new都必须是deleted。使用智能指针(shared_ptrunique_ptr)将通过执行自动清理来提供很大帮助。

是否有可能?对不惜一切代价?不可以。动态分配通常是解决问题的最简单方法。

但是,确实要不惜一切代价避免使用new。依靠make_sharedmake_unique来生成单个对象,而像std::vector这样的容器可以生成多个对象。没有任何理由让你使用delete

您总是可以从两个向量来处理此类问题;可维护性/可读性和性能。有时第二个很重要,有时不重要。有时你不在乎第一个。然而,在这种情况下,我想说,将尽可能多的东西放在自动内存("堆栈")和动态内存("堆")上(大多数时候)在这两方面都是有利的。

与更改堆栈指针相比,分配动态内存的速度太慢了,几乎没有关于性能的争论。但是,如果你在动态内存中分配了不离开其作用域的东西,你必须小心在作用域之后释放内存,如果你忘记了,你不仅会泄漏内存,而且很可能会破坏正确性,因为析构函数没有被调用。在这方面,它的可维护性较差。

对每一个new都持怀疑态度,并认为它很臭,这会有所帮助。它可以防止你不必要地过度使用。

是的,您应该尽可能避免堆分配。在您提到的情况下,您应该使用标准库提供的容器,如std::vector<byte>,如注释中所建议的那样。

如果您绝对必须在堆上存储一些东西,请使用RAII(资源获取即初始化),这意味着(堆栈上的)对象在构造时执行获取,在销毁时释放资源。由于C++11有std::unique_ptr,或者更复杂的std::shared_ptr(与std::weak_ptr一起),如果您需要一个资源的多个所有者:

std::unique_ptr<int> unique(new int(42));
auto sp = std::make_shared<int>(42);

如果你还不能使用C++11,几乎每个库都会为你提供某种智能指针——boost会给你scoped_ptr/scoped_arrayshared_ptr/shared_arrayweak_ptrintrusive_ptr;Poco给你AutoPtr等。C++03中甚至有一个智能指针,auto_ptr,但我强烈反对使用它;现在不赞成使用它,而且它有非常奇怪的复制行为,因为C++03不支持右值引用/移动语义。

动态分配的一些问题:

  1. 内存泄漏(如果您忘记删除对象)
  2. 大量的分配/释放请求——另一方面,堆栈分配只是在调用函数时移动堆栈指针一次。一些自动变量甚至可以驻留在寄存器中而不占用内存
  3. 堆碎片

基本上,只有在用尽所有其他选项的情况下才使用堆。