【问题标题】:C++ : Association, Aggregation and CompositionC++:关联、聚合和组合
【发布时间】:2014-11-04 22:15:17
【问题描述】:

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

我的理解是:

  • 关联: Foo 有一个指向 Bar 对象的指针作为数据成员
  • 聚合: Foo 有一个指向 Bar 对象的指针,并且 Bar 的数据被深度复制到该指针中。
  • 组合: 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;
    }
};

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

【问题讨论】:

  • programmers.stackexchange.com 更适合回答这个问题。
  • 聚合和关联的区别is subtle和它们在c++中的实现方式一般是一样的。
  • 我记得我在学校时对同样的术语感到非常困惑。然后,当我进入工作团队时,用于讨论对象关系的唯一术语是 is-a、has-a、继承和组合。也就是说,我知道 UML 有不同的聚合和组合符号,但我从未真正见过有人区分这两者。所以,在你的课程中找到答案,让你得到正确的答案,但不要过分强调。

标签: c++ oop associations aggregation ooad


【解决方案1】:

我将忽略聚合。它是not a very clearly defined concept,在我看来,它引起的混乱比它的价值还要多。作文和联想就够了,Craig Larman 这么告诉我的。这可能不是您的讲师正在寻找的答案,但无论如何它不太可能在 C++ 中与 Association 有任何不同。

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

作曲

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

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

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

使用指针实现组合也可能是有原因的。例如Bar 可能是多态类型,而您在编译时不知道具体类型。或者您可能想要转发声明Bar 以最小化编译依赖(参见PIMPL idiom)。或者这种关系的多重性可能是 1 到 0..1,并且您需要能够拥有一个空 Bar。当然,因为这是 Composition Foo 应该拥有 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,您可能希望使用std::shared_ptr

联想

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

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

当然,您需要确信Bar 会在Foo 使用它时仍然存在,否则您会有一个悬空指针。如果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";
  }
};

现在Foo 可以使用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 实现关联,但我认为这应该是最后的手段。

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

【讨论】:

    猜你喜欢
    • 2012-07-01
    • 2016-06-27
    • 1970-01-01
    • 2013-05-14
    • 2012-09-18
    • 2012-11-14
    • 2014-03-24
    • 2017-01-20
    • 2013-01-28
    相关资源
    最近更新 更多