我目前正在尝试了解如何在 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_ptr
与std::make_shared
而不是new()
和shared_ptr
的构造函数shared_ptr<Parent> parent = std::make_shared<Parent>();
使用现代编译器和库,这可以节省对
new()
的调用.两者
shared_ptr
和weak_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/