jeudi 5 mai 2016

What is the correct way to implement Aggregation in modern C++?

Context:

It is common that a situation arise in which I require aggregation: An object make use of another object without owning it. That is, some main module will create and share an object for/to others.

However, I did not found, up to now, a correct way to implement it.

Previous research:

1) C-like pointers: The shared object is provided as a pointer. The problem is that it is up to the programmer to manage correctly the order of creation, sharing and deletion, which could easily carry dangling pointers.

int main()
{
    A a;
    B b(&a);
    return 0;
}

2) Weak pointers: Using a shared/weak pointer solve the problem of dangling pointers. The main problem is: it avoid stack objects to be used. It also relay on exceptions which in the best case is a polemic topic, in the worst case is just not possible (kernel/low-level code).

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) Shared pointers: Using only shared pointer has the same problem than (2), but additionally the ownership of A is unknown: that break several design guidelines.

4) Object reference: Sharing a reference to the object solve dangling issue, and is probably the simplest solution. On the other hand, it force the aggregated object to be passed in the constructor, it avoid assignment operators, and in general limit the design.

int main()
{
    A a;
    B( a );
}

5) Never aggregate, just pass as argument. Even if possible, this increase drastically the number of parameters to some functions. It is, in my opinion, too much additional complexity.

6) Singleton pattern Singleton allows to access a single object from several modules. But, this only allows one instance to be shared, and it goes against several design guide-lines.

Question:

What is the correct way to implement aggregation in modern C++?

Ideal goals would be:

  • Make easy to maintain the code, and avoid errors.
  • Flexibility to use it in different ways (e.g. stack/heap)
  • Not too much additional complexity or obscure code.

Aucun commentaire:

Enregistrer un commentaire