将智能指针用作编程标准

Usage of Smart Pointers as a Programming Standard?

本文关键字:编程 标准 智能 指针      更新时间:2023-10-16

我越来越多地听说,尽管我已经实现了有效的内存泄漏系统,但我应该使用智能指针而不是裸指针

关于使用智能指针,正确的编程方法是什么?即使我检查了分配的内存块上的内存泄漏,它们真的应该被使用吗?这还取决于我吗?如果我不使用它们,这会被认为是编程的弱点吗?

如果强烈建议使用智能指针(例如:std::auto_ptr),我应该使用它们而不是每个裸指针吗?

您应该使用RAII来处理所有资源分配。

智能指针只是该规则的一个常见特例。

智能指针不仅仅是shared_ptr。存在具有不同所有权语义的不同智能指针。使用适合您需求的。(主要的是scoped_ptrshared_ptrweak_ptrauto_ptr/unique_ptr(在可用的情况下更喜欢后者)。根据编译器的不同,它们可能作为TR1的一部分在标准库中可用,也可能根本不可用,在这种情况下,您可以通过Boost库获得它们。

是的,你绝对应该使用这些。它不会让你付出任何代价(如果做得正确,你的性能将为零),它会给你带来很多好处(内存和其他资源会自动释放,你不必记得手动处理,使用资源的代码会变得更短、更简洁)

请注意,并不是每个指针使用都代表某种资源所有权,因此也不是所有的原始指针使用都是错误的。如果你只需要指向别人拥有的对象,那么一个原始指针是非常合适的。但是,如果拥有该对象,那么您应该通过赋予类本身RAII语义或将其封装在智能指针中来获得它的适当所有权。

不能盲目地用std::auto_ptr代替每个原始指针。特别是,auto_ptr在转让时转让所有权,这对某些目的来说很好,但对其他目的来说绝对不是。

存在多种智能指针(例如,shared_ptr、weak_ptr、auto_ptr/unique_ptr等)是有原因的。每种指针都实现不同的目的。"原始"指针的一个主要弱点是它有很多不同的用途(之所以具有这种多功能性,很大程度上是因为它对任何一个目的几乎没有帮助)。聪明的指针往往更专业,这意味着他们可以更聪明地做好一件事,但也意味着你必须为这份工作选择合适的指针,否则它最终会把错误的事情完全搞砸。

智能指针允许自动定义它所引用对象的生命周期。这是需要理解的主要内容。

因此,不,您不应该在任何地方都使用智能指针,只有当您希望自动化对象的生命周期,而不是让一个对象从出生到死亡管理这些对象时。它就像任何工具一样:它解决特定类型的问题,而不是所有的问题。

对于每个对象,你应该考虑它将经历的生命周期,然后选择一个最简单、正确、有效的解决方案。有时它会被shared_ptr,因为您希望该对象由多个组件使用,并在不再使用时自动销毁。有时您只需要当前作用域/父对象中的对象,所以scoped_ptr可能更合适。有时您只需要实例的一个所有者,因此unique_ptr是合适的。也许你会发现这样的情况,你知道一种算法可以定义/自动化对象的生命周期,所以你会为它编写自己的智能指针。

举个相反的例子,使用池禁止您使用smart_ptr。在这种特殊情况下(但在嵌入式软件中很常见),裸指针可能是一种更受欢迎的简单高效的解决方案。

有关更多解释,请参阅(我的)回答:https://softwareengineering.stackexchange.com/questions/57581/in-c-is-it-a-reflection-of-poor-software-design-if-objects-are-deleted-manuall/57611#57611

即使我检查了分配的内存块上的内存泄漏,它们真的应该被使用吗


智能指针的全部目的是,它帮助您实现RAII(SBRM),这基本上让资源本身承担其解除分配的责任,并且资源不必明确地依赖您记住来解除分配。

如果我不使用它们,这会被认为是编程的弱点吗

NO,
如果您不使用智能指针(RAII),那么自己明确管理资源并不是一个弱点,而是一种不便或不必要的麻烦。实现RAII的智能指针的目的是提供高效、轻松的资源处理方式,如果你不使用它,你就不会使用它。强烈建议使用它纯粹是因为它提供了许多优势。

如果强烈建议使用智能指针 (例如:std::auto_ptr),我应该使用它们而不是每个裸指针吗


你应该尽可能地使用智能指针,因为使用它们没有缺点,只是有很多优点
不要使用auto_ptr,因为它已经被弃用了!!根据需要,您可以使用各种其他智能指针。你可以参考上面的链接来了解更多关于它们的信息。

这是一个棘手的问题,而且目前有一种模式到处使用智能指针并不会让事情变得更容易。聪明的指针在某些情况下会有所帮助,但你肯定不能在任何地方使用它们,不用思考。有很多不同的类型聪明的指针,你必须考虑哪一个是合适的在任何情况下;即便如此,你的大多数指针(至少在典型的我工作过的领域中的应用程序)应该是原始指针。

无论采用何种方法,都有几点值得一提:

  • 除非迫不得已,否则不要使用动态分配应用程序,仅需要动态分配的内容是具有特定生存期的对象,由应用程序决定思维方式不要对具有值语义的对象使用动态分配。

  • 关于实体对象应用程序域:应该根据到程序逻辑。不管是否有指向不管他们是不是。如果它们的破坏造成了问题,那么你就有了程序逻辑中的某个错误(未正确处理事件,等等),并且使用智能指针不会改变任何事情。

实体对象的一个典型示例可能是服务器,在客户端连接时创建,在客户端断开连接。在许多此类情况下,最适当的管理将是delete this,因为它是将接收的连接断开连接事件。(包含指向此类对象的指针的对象必须向其注册,以便了解其摧毁但这样的指针纯粹是为了导航,不应该是聪明的指针。)

当人们试图使用智能指针时,你通常会发现什么所有都是内存泄漏;典型的参考计数器不会处理循环,当然,典型的应用程序充满了循环:Connection将指向与其连接的Client,并且CCD_ 13将包含其所连接的CCD_。如果智能指针是boost::shared_ptr,也有一个确定的悬挂指针的风险:创建两个指针要容易得多boost::shared_ptr到同一地址(这导致两个计数器参考文献)。

如果强烈建议使用智能指针(例如:std::auto_ptr),我应该使用它们而不是每个裸指针吗?

在我看来,是的,你应该为你拥有的每个指针使用它。

以下是我对C++中的资源管理的看法(不同意我的看法):

  1. 良好的资源管理需要从所有权的角度进行思考
  2. 资源应该由对象管理(RAII)
  3. 通常,单一所有权优先于共享所有权
  4. 理想情况下,创建者也是对象的所有者。(但是,在某些情况下,所有权转移是有序的。)


这导致了以下做法:

  1. boost::scoped_ptr设为本地变量和成员变量的默认选项。请记住,对成员变量使用scoped_ptr会使类不可复制。如果你不想这样,请看下一点。

  2. boost::shared_ptr用于容器或启用共享所有权:

    // Container of MyClass* pointers:
    typedef boost::shared_ptr<MyClass> MyClassPtr;
    std::vector<MyClassPtr> vec;

  3. std::auto_ptr(C++03)可用于所有权转移。例如,作为工厂或克隆方法的返回值:

    // Factory method returns auto_ptr
    std::auto_ptr<Button> button = Button::Create(...);

    // Clone method returns auto_ptr
    std::auto_ptr<MyClass> copy = obj->clone();

    // Use release() to transfer the ownership to a scoped_ptr or shared_ptr
    boost::scoped_ptr<MyClass> copy(obj->clone().release());

  4. 如果你需要存储一个你不拥有的指针,那么你可以使用一个原始指针:

    this->parent = inParentObject;

  5. 在某些情况下,需要boost::weak_pointer。有关详细信息,请参阅文档。

通常,您应该更喜欢智能指针,但也有几个例外。

如果您需要重铸指针,例如提供const版本,那么使用智能指针几乎是不可能的。

智能指针用于控制对象的生存期。通常,当您传递指向函数的指针时,该函数不会影响生存期;该函数不会尝试删除对象,也不会存储指针的副本。在函数返回之前,调用代码无法删除对象。在这种情况下,哑指针是完全可以接受的。

是。假设您有可用的C++0x,请使用unique_ptrshared_ptr(视情况而定)来包装new的所有原始指针。在make_shared的帮助下,shared_ptr具有很高的性能。如果你不需要引用计数,那么unique_ptr会让你获得更好的性能。它们在集合和其他auto_ptr是哑指针的情况下都表现得很好。

在任何地方使用智能指针(shared_ptr或其他)都是个坏主意。使用shared_ptr来管理对象/资源的生存期是好的,但将它们作为参数传递给函数等不是一个好主意。这增加了循环引用和其他极难跟踪的错误的可能性(个人经验:如果每个函数调用都改变了引用计数,那么试着在200万行代码中找出谁不应该占用资源——你最终会认为做这种事情的人是m***ns)。最好传递一个原始指针或引用。当与惰性实例化相结合时,情况会更糟。我建议开发人员应该知道他们编写的对象的生命周期,并使用shared_ptr来控制它(RAII),但不要将shared_ptr的使用扩展到它之外。