上下文
通常会出现需要聚合的情况:一个对象在没有拥有它的情况下使用另一个对象。也就是说,一些主模块将为其他人创建和共享一个对象。
但是,到目前为止,我还没有找到实现它的正确方法。
之前的研究:
1)类似C的指针: 共享对象作为指针提供。问题在于程序员可以正确地管理创建,共享和删除的顺序,这可能很容易带来悬空指针。
int main()
{
A a;
B b(&a);
return 0;
}
2)弱点: 使用共享/弱指针解决悬空指针的问题。主要问题是:它避免使用堆栈对象。它还会对异常进行中继,在最好的情况下,这是一个极端主题,在最坏的情况下是不可能的(内核/低级代码)。
int main()
{
std::shared_ptr<A> a(new A());
std::weak_ptr<A> wa = a; // optional, but required to show the weak_ptr use
B b(wa);
}
3)共享指针: 仅使用共享指针与(2)具有相同的问题,但另外A的所有权是未知的:这违反了几个设计准则。
4)对象参考: 共享对象的引用解决了悬空问题,可能是最简单的解决方案。另一方面,它强制聚合对象在构造函数中传递,它避免赋值运算符,并且通常限制设计。
int main()
{
A a;
B( a );
}
5)永远不要聚合,只要作为参数传递。 即使可能,这也会大大增加某些功能的参数数量。在我看来,这太复杂了。
6)单身人士模式 Singleton允许从多个模块访问单个对象。但是,这只允许共享一个实例,并且它违背了几个设计指南。
问题:
在现代C ++中实现聚合的正确方法是什么?
理想的目标是:
答案 0 :(得分:2)
正如许多评论者所指出的那样,你的问题没有一个解决方案。但是,考虑到C ++设计模式的人们已经详细讨论了这种事情。关于更好地处理悬空指针的一个很好的讨论(包括其中一个评论者的建议,template <class T> using non_owning_ptr<T> = T*
),我建议Bjarne Stroustrup的talk关于C ++核心规则和指南,特别是从链接开始的段给出。 Herb Sutter的后续行动talk在这方面也很有用,介绍了静态分析工具的一些想法,以执行这些规则。
我知道这不是你正在寻找的神奇解决方案,但是对于它的价值,它似乎是人们方式更智能和方式的解决方案比我想象的更有经验的C ++。老实说,这就是Bjarne Stroustrup最近在这些规则和指导方面花费大部分时间的原因:C ++本身并不存在对这些事情的完美解决方案,但是在一些限制条件下,我们可以更接近。
答案 1 :(得分:2)
我需要聚合:一个对象在不拥有它的情况下使用另一个对象。
这不是&#34;聚合&#34;的定义。我熟悉的。但就本篇文章而言,我将使用它。
如果没有在C ++类型系统中拥有对象,就无法表达一个对象访问对象的能力。对于表达这种想法的类型,实现这种关系的方法太多了。
让我们说我们有一些实例A可以访问实例B但不拥有B.所以,你如何定义这两个实例之间的关系?如果在没有首先通知A的情况下销毁B,则这种关系是不安全的。那么,我们如何确保?
实际上有几十种方法。也许B保存在shared_ptr
中,因此A可以在weak_ptr
中保留其引用,从而在B不再存在时获得通知。也许有一些特定的对象C拥有A和B,并将确保这一点。也许代码的一般结构使得B在A之前不可能被销毁。也许B本身知道它何时被链接到A并且将在其析构函数中通知A.或许代码很脆弱,但每个编写代码的人都非常谨慎,以确保A不会比B更长。
没有单一的&#34;正确的方法来实现它&#34;,因为&#34;它&#34;不是一回事。这是无数可能的关系。这些关系都不一定是错的;他们中的大多数甚至不一定是坏代码。它们只是类型之间的隐式关系。
Library Fundamentals v1包含observer_ptr<T>
,它只是一个指针包装器;它没有传达所有权语义。但是,核心C ++指南建议raw pointers be used for most of these use-without-ownership relations;而是建议使用owner<T>
注释拥有某些内容的指针。