C++:关联,聚合和组合

Tal*_*389 21 c++ oop ooad associations aggregation

我开始研究OOAD,我很难找到一个C++即会说明如何代码示例Association,AggregationComposition通过编程来实现.(到处都有几个帖子,但它们与C#或java有关).我确实找到了一两个例子,但它们都与我的导师的指示相冲突,我很困惑.

我的理解是:

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

这就是我实现它的方式:

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

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


//COMPOSTION
class Bar
{
    Baz baz;
};
class Foo
{
    Bar bar;
    Foo(Baz baz)
    {
        bar.baz=baz;
    }
};
Run Code Online (Sandbox Code Playgroud)

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

Chr*_*rew 16

我要忽略聚合.它不是一个非常明确定义的概念,在我看来它会导致更多的混乱而不是它的价值.组成和协会已经足够了,Craig Larman告诉我了.它可能不是您的教师所寻求的答案,但无论如何都不可能在C++中实现.

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

组成

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

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

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

可能还有理由使用指针实现组合.例如,Bar可能是多态类型,并且您在编译时不知道具体类型.或者您可能希望转发声明Bar以最小化编译依赖性(请参阅PIMPL惯用法).或者这种关系的多样性可能是1到0..1,你需要能够得到一个null 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)) {}
};
Run Code Online (Sandbox Code Playgroud)

我用std::unique_ptr在这里,因为Foo是唯一拥有Bar,但你可能想使用std::shared_ptr,如果其他一些对象需要一个std::weak_ptrBar.

协会

通常会使用指针实现关联:

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

当然,你需要确信BarFoo使用它时它会活着,否则你会有一个悬垂的指针.如果生命周期Bar不太清楚,那么a 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";
  }
};
Run Code Online (Sandbox Code Playgroud)

现在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
Run Code Online (Sandbox Code Playgroud)

在某些情况下,所有权Bar真的不明确,协会可以使用a实现,std::shared_ptr但我认为这应该是最后的手段.

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