关于new和delete的使用,以及Stroustrup的建议

About the usage of new and delete, and Stroustrup's advice

本文关键字:以及 Stroustrup new delete 关于      更新时间:2023-10-16

关于new和delete的使用,以及Stroustrup的建议...

他说了这样的话(但不完全是,这是我对他的书的笔记):

经验法则是,new属于构造函数和类似操作,delete属于析构函数。此外,new通常用于资源句柄的参数中。否则,请避免使用newdelete,请改用资源句柄(智能指针)。

我想知道 C++11 更有经验的人是否真的应用了这个。

我对此的印象是,哇,这似乎是一个非常酷的规则。 但后来我开始怀疑,至于任何一般规则。在一天结束时 您最终将在必要时使用"新建"和"删除"。但也许这个规则 是我不知道的好准则。

这是一个很好的规则。实际上,通过使用适当的make_函数,可以避免在智能指针的参数中使用new。例如,代替:

std::shared_ptr<int> p(new int(5));

您通常可以执行以下操作:

auto p = std::make_shared<int>(5);

这也具有更异常安全的好处。虽然std::make_unique尚不存在,但计划进入C++14(它已经在工作草案中)。如果你现在想要它,有一些现有的实现。

您可以更进一步,甚至避免在构造函数和析构函数中使用newdelete。如果始终将动态分配的对象包装在智能指针中,即使它们是类成员,也根本不需要管理自己的内存。请参阅零法则。这个想法是,实现任何形式的所有权语义(SRP)不是你的类的责任 - 这就是智能指针的用途。那么理论上你永远不必编写复制/移动构造函数、复制/移动赋值运算符或析构函数,因为隐式定义的函数通常会做适当的事情。

看起来更像是一个民意调查而不是一个问题,但它是这样的:在应用程序代码中,我通常根本不使用new。由于我们的编码准则,代码确实使用指针,但这些"裸"指针实际上都没有转移所有权。所有对象都归其他对象所有。

公平地说,当需要分配对象时,分配通常使用道德上等同于std::make_shared<T>(...)的东西,有时确实出现在应用程序代码中。这种相当彻底的new(或类似)缺失的一个主要原因是对象通常使用有状态分配器进行分配,而不是通过资源管理器这样做实际上恰好相当复杂。因此,在应用程序代码中使用new或其放置版本进行直接内存分配的位置很小。

在某些基础设施代码中,特别是在创建自定义容器时,情况略有不同:那里分配了内存(来自分配器并使用放置new进行初始化)。但是,即使存在内存分配和对象初始化的任何结果也会立即传递给资源管理器。基本上,我无法应对明确的资源管理,使用资源管理器只是减轻了我必要的工作。

我的想法是,每个资源都应该由某些东西拥有。所有者是负责清理的人。通常这个所有者是某种智能指针,但即使是 std::vector 也是一个资源的所有者:存储其连续元素的内存块。此建议不仅适用于内存,也适用于任何资源,例如文件描述符、数据库句柄、互斥体等......

当您调用 new 并在代码的某些部分(不是包装类)中手动删除时,程序员将成为资源所有者。有了所有权,就有责任清理自己。现在,您和所有后来的维护程序员必须确保新代码路径之后的所有代码路径最终都会导致删除。即使对于简单的功能,这也很容易出错。对于异常,除非您小心地将所有内容包装在 try catch 块中,否则几乎是不可能的,这会导致运行时性能下降,并使用额外的范围和不必要的异常逻辑污染您的代码。最后,即使你做对了,你也只是浪费了很多时间做这种乏味的资源管理工作。编译器是可以为您完成这项工作的工具,使用它。

最糟糕的情况是,当某个子系统分配资源时,它会在应用程序中传递,而其他一些遥远的子系统会释放它。在这种情况下,可能的代码路径数很难处理。对于一个人来说,推理和信任是非常困难的,如果不是不可能的话。在我看来,这种编程风格是无法维护的。您过去使用过多少个充满内存错误的 C 项目,尤其是在很少(如果从未执行)的错误处理路径上?我处理的比我更想看到的更多了。

C有手动内存管理,Java和其他有垃圾回收。C++有 RAII。它与C一样高效,几乎与垃圾收集一样安全。

我的规则很简单,如果你发现自己手动清理任何资源,你刚刚写了一个错误。