说我有一个Foobar
使用(取决于)class的类Widget
。好的时候,将Widget
wolud声明为中的字段Foobar
,或者如果需要多态行为,则可以声明为智能指针,并将其在构造函数中初始化:
class Foobar {
Widget widget;
public:
Foobar() : widget(blah blah blah) {}
// or
std::unique_ptr<Widget> widget;
public:
Foobar() : widget(std::make_unique<Widget>(blah blah blah)) {}
(…)
};
而且我们已经准备就绪。不幸的是,今天,Java的孩子们会嘲笑我们一旦看到它,这是理所当然的,因为它的夫妇Foobar
和Widget
在一起。解决方案看似很简单:应用“依赖注入”以使依赖构造脱离Foobar
类。但是,C ++迫使我们考虑依赖关系的所有权。想到三个解决方案:
唯一指针
class Foobar {
std::unique_ptr<Widget> widget;
public:
Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
(…)
}
Foobar
要求将所有权Widget
转移给它。这具有以下优点:
- 对性能的影响可以忽略不计。
- 它很安全,因为它可以
Foobar
控制生命周期Widget
,因此可以确保Widget
它不会突然消失。 - 它是安全的,
Widget
不会泄漏,并且在不再需要时会被适当破坏。
但是,这是有代价的:
- 它对如何使用
Widget
实例设置了限制,例如,不能使用堆栈分配Widgets
,Widget
不能共享。
共享指针
class Foobar {
std::shared_ptr<Widget> widget;
public:
Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
(…)
}
这可能是与Java和其他垃圾收集语言最接近的等效语言。优点:
- 更通用,因为它允许共享依赖项。
- 保持
unique_ptr
解决方案的安全性(第2点和第3点)。
缺点:
- 不涉及共享时浪费资源。
- 仍然需要堆分配,并且不允许堆栈分配的对象。
普通的观察指针
class Foobar {
Widget *widget;
public:
Foobar(Widget *w) : widget(w) {}
(…)
}
将原始指针放在类中,并将所有权负担转移给其他人。优点:
- 尽可能简单。
- 通用,接受任何
Widget
。
缺点:
- 不再安全了。
- 引入了另一个实体,负责双方的所有权
Foobar
和Widget
。
一些疯狂的模板元编程
我能想到的唯一好处是,我可以在软件运行时阅读所有我没有找到时间的书;)
我倾向于第三个解决方案,因为它是最通用的,Foobars
无论如何都必须进行管理,因此管理Widgets
是简单的更改。但是,使用原始指针困扰我,另一方面,智能指针解决方案令我感到不舒服,因为它们使依赖项的使用者限制了如何创建依赖项。
我想念什么吗?还是仅仅是C ++中的依赖注入是不平凡的?类应该拥有它的依赖关系还是仅仅观察它们?
Foobar
是唯一的所有者?在旧情况下很简单。但是,正如我所看到的,DI的问题在于,它使类与依赖关系的构造脱钩,也使类与这些依赖关系的所有权脱钩(因为所有权与构造相关)。在Java等垃圾收集环境中,这不是问题。在C ++中,是这样。
std::unique_ptr
是可行的方法。您可以std::move()
用来将资源的所有权从高级范围转移到类。