当前位置: 首页 > 知识库问答 >
问题:

C:关联、聚合和组合

岳迪
2023-03-14

我开始研究 OOAD,我很难找到一个 C 代码示例来说明如何以编程方式实现关联聚合组合。(到处都有几篇文章,但它们与 C# 或 Java 有关)。我确实找到了一两个例子,但它们都与我的导师的指示相冲突,我很困惑。

我的理解是,在:

    < li >关联:Foo有一个指向Bar对象的指针作为数据成员 < li >聚合:Foo有一个指向Bar对象的指针,Bar的数据被深度复制到该指针中。 < li >组成:Foo有一个Bar对象作为数据成员。

这就是我如何实现它的:

class Bar
{
    Baz baz;
};

//ASSOCIATION (with Bar)
class Foo
{
    Bar* bar;
    void setBar(Bar* _bar)
    {
        bar = _bar;
    }
};

//AGGREGATION (with Bar)
class Foo
{
    Bar* bar;
    void setBar(Bar* _bar)
    {
        bar = new Bar;
        bar->baz = _bar->baz;
    }
};


//COMPOSTION (with Bar)
class Foo
{
    Bar bar;
    Foo(Baz baz)
    {
        bar.baz = baz;
    }
};

这是对的吗?如果没有,那么应该如何做呢?如果您还给我一本书中的代码参考,我将不胜感激(以便我可以与我的老师讨论)

共有1个答案

夏侯承恩
2023-03-14

我将忽略聚合。它不是一个非常明确定义的概念,在我看来,它造成的混乱超过了它的价值。组合和关联就足够了,克雷格·拉曼告诉我。这可能不是你的老师想要的答案,但无论如何,它不太可能在C中实现,与关联有任何不同。

没有一种方法可以实现组合和关联。如何实现它们将取决于您的需求,例如关系的多样性。

实现组合的最简单方法是使用一个简单的Bar成员变量,就像您建议的那样。我要做的唯一更改是初始化构造函数成员初始化器列表中的bar

// COMPOSITION - with simple member variable
class Foo {
 private:
  Bar bar;
 public:   
  Foo(int baz) : bar(baz) {}
};

使用构造函数初始化列表来初始化成员变量通常是一个好主意,这样会更快,而且在某些情况下,像const成员变量,这是初始化它们的唯一方法。

使用指针实现复合可能也是有原因的。例如,<code>Bar</code>可能是一个多态类型,而您在编译时不知道具体的类型。或者,您可能希望转发声明<code>Bar</code>以最小化编译依赖性(请参见PIMPL习惯用法)。或者这个关系的多重性是1到0..1,你需要有一个空的<code>Bar</code>。当然,因为这是CompositionFoo应该拥有Bar,而在现代C 11/C 14世界中,我们更喜欢使用智能指针,而不是拥有原始指针:

 // COMPOSITION - with unique_ptr
class Foo {
 private:
  std::unique_ptr<Bar> bar;
 public:   
  Foo(int baz) : bar(barFactory(baz)) {}
};

我在这里使用了std::unique_ptr,因为FooBar的唯一所有者,但如果其他对象需要std::weak_ptrBar时,您可能需要使用td::shared_ptr

关联通常使用指针实现,就像您所做的那样:

// Association - with non-owning raw pointer
class Foo {
 private:
  Bar* bar;
 public:   
  void setBar(Bar* b) { bar = b; }
};

当然,您需要确信BarFoo使用时会活着,否则您就有一个悬空指针。如果Bar的生命周期不太清楚,那么std::weak_ptr可能更合适:

// Association - with weak pointer
class Foo {
 private:
  std::weak_ptr<Bar> bar;
 public:   
  void setBar(std::weak_ptr<Bar> b) { bar = std::move(b); }
  void useBar() {
    auto b = bar.lock();
    if (b)
      std::cout << b->baz << "\n";
  }
};

现在< code>Foo可以使用< code>Bar而不用担心会留下悬空指针:

 Foo foo;
 {
   auto bar = std::make_shared<Bar>(3);
   foo.setBar(bar);
   foo.useBar(); // ok
 }  
 foo.useBar(); // bar has gone but it is ok

在某些情况下,Bar的所有权确实不清楚,可以使用std::shared_ptr实现关联,但我认为这应该是最后的手段。

关于您使用柱线指针的深层副本实现聚合。我不会说这是一个典型的实现,但正如我所说,这取决于您的要求。您确实需要确保在 Foo 析构函数中对酒吧成员指针调用 delete,否则会出现内存泄漏(或使用智能指针)。

 类似资料:
  • 我试图确认我对关联、聚合的代码外观的理解 聚合:哈斯-a。它具有另一种类型的现有对象 组成:由另一个对象组成 协会:我对此有两种看法。 > 当一个类与另一个类相关联时。因此,上述两个都是关联的示例。 关联是一种较弱的聚合形式,其中类不保留对其接收的对象的引用。 我的理解正确吗?我在这里和这里阅读了相互矛盾的文章,所以我真的不确定该遵循哪一个。我的理解似乎符合第一个环节。我觉得第二个环节是错误的,或

  • 我理解聚合和组合之间的区别,但我在联想方面有点挣扎。我目前的理解是,当“它们相互使用”时,类之间存在关联,例如,在方法调用期间将一个对象传递给另一个对象。另请参阅: http://www.codeproject.com/Articles/330447/Understanding-Association-Aggregation-and-Composit 这两个对象都是独立存在的,和聚合不同,任何对象

  • 问题内容: 我试图理解这些术语的含义。我举了一些例子,例如: 汇总:Facebook 有一个 用户 组成:facebook 中的 每个用户 都有一个 会话。 协会:人们 使用 浏览器 但是我对 具有 和 使用我的 示例感到困惑。为什么不能是用户 使用 Facebook帐户或Facebook 使用 会话来认证用户? 就OOP而言,这是错误的吗?我在哪里想念这个概念? 问题答案: 该 使用 关系意味着

  • 我在Stackoverflow上看到了很多解释关系区别的帖子:关联、聚合、组合和继承,并附有例子。然而,更具体地说,我对每种方法的优点和缺点,以及什么时候一种方法对手头的任务最有效感到困惑。这是我一直无法找到一个好答案的事情。 为了与论坛的指导方针保持一致,我没有问为什么人们个人更喜欢使用继承而不是组合。我特别感兴趣的是每种方法中的任何客观优点/缺点,尽管听起来很强。一、 e.一种方法是创建比另一

  • 因此,我对UML图中的关联、聚合和组合有一些疑问。以下是一些场景: > 产品评审对产品评审的评级组成。这意味着对于每一个产品评审等级必须有产品评审?如果产品评审不存在,评审评级就没有意义。 客户NRIC协助推车和订货。我们不能使用聚合,因为如果客户不存在,购物车和订单也不存在。 有人能帮我检查一下我的关系是否正确吗?因为我对聚合和关联有点困惑,所以用关联来链接所有的表是好的吗。我不知道什么时候该用

  • 我在UML中遇到了和关系的一些问题,我确实理解整体/部分关系,所以如果一个类不能没有它的整体而存在,那么它就成为一个强的组合关系,如果它仍然可以没有它的整体而存在,那么它就成为一个弱的聚合关系。 然而,当处理真正的软件需求时,有时会变得更加棘手。我有一个下面的类图,包括所有必要的属性、操作和特性,它们都正确地显示在UML标准中,但是我不确定我的关系: 1 接口和 6 类 有人可以确认我的关系是否正