为什么 C++ 程序员应该尽量减少使用“new”?

2023-12-06

我偶然发现堆栈溢出问题使用 std::list 时 std::string 内存泄漏, and 评论之一说:

停止使用new非常。我看不出你在任何地方使用 new 的任何理由。您可以按值创建对象C++这是使用该语言的巨大优势之一。您不必分配堆上的所有内容。别再像一个人那样思考Java程序员。

我不太确定他的意思是什么。

为什么要通过值来创建对象C++尽可能频繁地进行,这对内部有什么影响?我是否误解了答案?


有两种广泛使用的内存分配技术:自动分配和动态分配。通常,每个内存区域都有一个相应的区域:堆栈和堆。

Stack

堆栈总是按顺序分配内存。它可以这样做,因为它要求您以相反的顺序释放内存(先进后出:FILO)。这是许多编程语言中局部变量的内存分配技术。它非常非常快,因为它需要最少的簿记,并且下一个要分配的地址是隐式的。

在 C++ 中,这称为自动存储因为存储空间会在范围结束时自动声明。一旦执行当前代码块(使用分隔{})完成后,该块中所有变量的内存都会自动收集。这也是此时此刻析构函数被调用来清理资源。

Heap

堆允许更灵活的内存分配模式。记账更复杂,分配也更慢。因为没有隐式释放点,所以必须手动释放内存,使用delete or delete[] (free在C)中。然而,没有隐式释放点是堆灵活性的关键。

使用动态分配的原因

即使使用堆速度较慢并且可能导致内存泄漏或内存碎片,动态分配也有非常好的用例,因为它的限制较少。

使用动态分配的两个主要原因:

  • 您不知道编译时需要多少内存。例如,当将文本文件读入字符串时,您通常不知道文件的大小,因此在运行程序之前无法决定分配多少内存。

  • 您想要分配在离开当前块后仍保留的内存。例如,您可能想编写一个函数string readfile(string path)返回文件的内容。在这种情况下,即使堆栈可以保存整个文件内容,您也无法从函数返回并保留分配的内存块。

为什么动态分配通常是不必要的

在 C++ 中,有一个简洁的构造,称为析构函数。此机制允许您通过将资源的生命周期与变量的生命周期对齐来管理资源。这种技术称为RAII这是C++的区别点。它将资源“包装”成对象。std::string就是一个完美的例子。这个片段:

int main ( int argc, char* argv[] )
{
    std::string program(argv[0]);
}

实际上分配了可变数量的内存。这std::string对象使用堆分配内存并在其析构函数中释放它。在这种情况下,你做了not不需要手动管理任何资源,仍然可以获得动态内存分配的好处。

特别是,在这个片段中它意味着:

int main ( int argc, char* argv[] )
{
    std::string * program = new std::string(argv[0]);  // Bad!
    delete program;
}

存在不需要的动态内存分配。该程序需要更多的输入(!)并带来忘记释放内存的风险。它这样做并没有明显的好处。

为什么应该尽可能经常使用自动存储

基本上,最后一段总结了这一点。尽可能经常使用自动存储可以使您的程序:

  • 打字速度更快;
  • 跑步时速度更快;
  • 不太容易出现内存/资源泄漏。

奖励积分

在引用的问题中,还有其他问题。特别是下面的类:

class Line {
public:
    Line();
    ~Line();
    std::string* mString;
};

Line::Line() {
    mString = new std::string("foo_bar");
}

Line::~Line() {
    delete mString;
}

实际上使用起来比下面的风险要大得多:

class Line {
public:
    Line();
    std::string mString;
};

Line::Line() {
    mString = "foo_bar";
    // note: there is a cleaner way to write this.
}

原因是std::string正确定义复制构造函数。考虑以下程序:

int main ()
{
    Line l1;
    Line l2 = l1;
}

使用原始版本,该程序可能会崩溃,因为它使用delete在同一根弦上两次。使用修改后的版本,每个Line实例将拥有自己的字符串instance,每个都有自己的内存,并且都将在程序结束时释放。

其他注意事项

广泛使用RAII由于上述所有原因,被认为是 C++ 中的最佳实践。然而,还有一个并非立即显而易见的额外好处。基本上,它比各个部分的总和更好。整个机制composes。它可以扩展。

如果您使用Line类作为构建块:

 class Table
 {
      Line borders[4];
 };

Then

 int main ()
 {
     Table table;
 }

分配四个std::string实例,四个Line实例,一Table实例和所有字符串的内容以及一切都会自动释放.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 C++ 程序员应该尽量减少使用“new”? 的相关文章

随机推荐