C++ : Association, Aggregation and Composition

后端 未结 1 787
迷失自我
迷失自我 2021-01-30 11:24

I\'m beginning to study OOAD and I\'m having difficulty finding a C++ code example that\'d illustrate how Association, Aggregation and

相关标签:
1条回答
  • 2021-01-30 11:42

    I'm going to ignore Aggregation. It is not a very clearly defined concept and in my opinion it causes more confusion than it is worth. Composition and Association are quite enough, Craig Larman told me so. It might not be the answer your instructor was looking for but it is unlikely to be implemented in C++ any differently to Association anyway.

    There is not one way of implementing Composition and Association. How you implement them will depend on your requirements, for example the multiplicity of the relationship.

    Composition

    The simplest way of implementing composition is using a simple Bar member variable much like you suggested. The only change I would make is to initialize the bar in the constructor member initializer list:

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

    It is generally a good idea to initialize member variables using the constructor initialization list, it can be quicker and in some cases like const member variables it is the only way to initialize them.

    There might also be a reason to implement composition using a pointer. For example Bar could be a polymorphic type and you don't know the concrete type at compile time. Or perhaps you want to forward declare Bar to minimize compilation dependencies (see PIMPL idiom). Or perhaps the multiplicity of this relationship is 1 to 0..1 and you need to be able to have a null Bar. Of course because this is Composition Foo should own the Bar and in the modern world of C++11/C++14 we prefer to use smart pointers instead of owning raw pointers:

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

    I've used std::unique_ptr here because Foo is the sole owner of Bar but you might want to use std::shared_ptr if some other object needs a std::weak_ptr to Bar.

    Association

    Association would usually be implemented using a pointer as you have done:

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

    Of course you need to be confident that Bar will be alive while Foo is using it otherwise you have a dangling pointer. If the lifetime of Bar is less clear then a std::weak_ptrmay be more appropriate:

    // 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";
      }
    };
    

    Now Foo can use the Bar without fear of being left with a dangling pointer:

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

    In some cases where ownership of Bar is really unclear an Association could be implemented using a std::shared_ptr but I think that should be a last resort.

    Regarding your implementation of Aggregation with a deep copy of a Bar pointer. I wouldn't have said that was a typical implementation but, as I said, it depends on your requirements. You do need to make sure you call delete on your bar member pointer in the Foo destructor though otherwise you have a memory leak (or use a smart pointer).

    0 讨论(0)
提交回复
热议问题