c++ - 如何在良好的 C++ API 中正确使用 shared_ptr

标签 c++ c++11

我目前正在尝试了解如何在 C++ API 中正确使用 C++11 的 shared_ptr 特性。我需要它的主要区域是容器类(例如场景图中的节点,它可能包含子节点列表和对父节点的引用以及类似的东西)。创建节点的拷贝不是一种选择,使用引用或指针是一件很痛苦的事,因为没有人真正知道谁负责破坏节点(当有人破坏了一个仍然被其他节点引用的节点时,程序将崩溃).

所以我认为在这里使用 shared_ptr 可能是个好主意。让我们看一下下面的简化示例(它演示了一个必须连接到父节点的子节点):

#include <memory>
#include <iostream>

using namespace std;

class Parent {};

class Child {
    private:
        shared_ptr<Parent> parent;
    public:
        Child(const shared_ptr<Parent>& parent) : parent(parent) {}
        Parent& getParent() { return *parent.get(); }
};

int main() {
    // Create parent
    shared_ptr<Parent> parent(new Parent());

    // Create child for the parent
    Child child(parent);

    // Some other code may need to get the parent from the child again like this:
    Parent& p = child.getParent();

    ...        

    return 0;
}

此 API 强制用户使用 shared_ptr 来创建子项和父项之间的实际连接。但在其他方法中,我想要一个更简单的 API,这就是为什么 getParent() 方法返回对父对象而不是 shared_ptr 的引用。

我的第一个问题是:这是 shared_ptr 的正确用法吗?还是有改进的余地?

我的第二个问题是:我如何正确应对空指针?因为 getParent 方法返回一个引用,用户可能认为它永远不会返回 NULL。但这是错误的,因为当有人将包含空指针的共享指针传递给构造函数时,它将返回 NULL。其实我不想要空指针。必须始终设置父对象。我该如何正确处理这个问题?通过在构造函数中手动检查共享指针,当它包含 NULL 时抛出异常?或者,还有更好的方法?也许是某种非空共享指针?

最佳答案

为您描述的目的使用共享指针是合理的,并且在 C++11 库中越来越普遍。

注意几点:

  • 在 API 上,取 shared_ptr作为参数强制调用者构造一个 shared_ptr .这绝对是一个很好的举措,因为受指派的所有权已经转移。在函数仅使用 shared_ptr 的情况下, 引用对象或 shared_ptr 可能是可以接受的

  • 您正在使用 shared_ptr<Parent>保留对父对象的反向引用,同时在另一个方向上使用一个。这将创建一个保留周期,导致对象永远不会被删除。一般来说,使用 shared_ptr当自上而下引用时,weak_ptr引用时。特别注意委托(delegate)/回调/观察者对象 - 这些几乎总是需要 weak_ptr给被叫者。如果 lambda 是异步执行的,您还需要注意它们。一个常见的模式是捕获 weak_ptr .

  • 通过引用而不是值传递共享指针是一个有赞成和反对论点的风格点。很明显,当通过引用传递时,您并没有传递所有权(例如,增加对象的引用计数)。另一方面,您也没有承担开销。以这种方式在引用对象下存在危险。在更实际的层面上,使用 C++11 编译器和标准库,按值传递应该导致移动而不是复制构造,并且无论如何几乎是免费的。但是,通过引用传递使调试变得相当容易,因为您不会重复进入 shared_ptr。的构造函数。

  • 构建您的 shared_ptrstd::make_shared而不是 new()shared_ptr的构造函数

    shared_ptr<Parent> parent = std::make_shared<Parent>();

    使用现代编译器和库,这​​可以节省对 new() 的调用.

  • 两者 shared_ptrweak_ptr可以包含 NULL - 就像任何其他指针一样。您应该始终养成在取消引用之前进行检查的习惯,可能是 assert()也很自由。对于构造函数的情况,您始终可以接受 NULL指针,而不是在使用点抛出。

  • 您可以考虑使用 typedef对于您的共享指针类型。有时使用的一种样式如下:

    typedef std::weak_ptr<Parent> Parent_P;

    typedef std::shared_ptr<Parent> Parent_WkP;

    typedef std::weak_ptr<Child> Child_P;

    typedef std::shared_ptr<Child> Child_WkP;

  • 了解在头文件中您可以转发声明 shared_ptr<Type> 也很有用没有看到 Type 的完整声明.这可以节省大量的标题膨胀

关于c++ - 如何在良好的 C++ API 中正确使用 shared_ptr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24355804/

相关文章:

c++ - A aaa 之间的区别;和一个 aaa();

c++ - 如何查看可执行文件中的宏定义值

c++ - 使相同的 C++ 类型别名不兼容

c++ - 寻找设计模式以减少虚拟方法的重载

c++ - 错误 : 'midfielder1' not declared in this scope

c++ - CField 类成员函数的 IsDirty 名称

c++ - 在 C++ 中测量 popcount 函数的时间

c++ - 如何轻松知道编译器选择了哪些特殊成员函数?

c++ - VC++ 自动说明符假定 vector<bool>::back 的引用限定符

c++ - 在 istream 中包装 vector<char> 时,tellg 和 seekg 不工作