tags:

views:

975

answers:

5

If I understand correctly we have at least 2 different ways of implementing composition. (Implementation with smart pointers is excluded case for simplicity. I don't use STL almost and have no desire to learn it.)

Let's have a look at Wikipedia example:

class Car
{
  private:
    Carburetor* itsCarb;
  public:   
    Car() {itsCarb=new Carburetor();}
    virtual ~Car() {delete itsCarb;}
};

So, it's one way - we have a pointer to object as private member. One can rewrite it to look like this:

class Car
{
  private:
    Carburetor itsCarb;
};

In that case we have an object itself as private member. (By the way, calling this entity as object am I write from the terminology point of view?)

In the second case it is not obligatory to implicitly call default constructor (if one need to call non-default constructor it's possible to do it in initializer list) and destructor. But it's not a big problem...

And of course in some aspects these two cases differ more appreciably. For example it's forbidden to call non-const methods of Carburetor instance from const methods of Car class in the second case...

Is there any "rules" to decide which one to use? Am I missed something?

Thanks in advance.

+2  A: 

Composition: prefer member when possible. Use a pointer when polymorphism is needed or when a forward declaration is used. Of course, without smart pointer, manual memory management is needed when using pointers.

stefaanv
+1  A: 

If Carb has the same lifetime as Car, then the non-pointer form is better, in my opinion. If you have to replace the Carb in Car, then I'd opt for the pointer version.

Richard Pennington
+3  A: 

I tend to prefer the first case because the second one requires you to #include Carburettor.h in Car.h. Since Carburettor is a private member you should not have to include its definition somewhere else than in the actual Car implementation code. The use of the Carburettor class is clearly an implementation detail and external objects that use your Car object should not have to worry about including other non mandatory dependencies. By using a pointer you just need to use a forward declaration of Carburettor in Car.h.

nico
This is the same in both cases, so I downvoted this.
Frederik Slijkerman
Can you be more specific, Im pretty sure you didn't get my point...
nico
It's _not_ the same in both cases. As nicolascormier pointed out, you can use a forward declaration to avoid including the header file. Maybe you should understand the answer before downvoting.
Tom Dalling
+3  A: 

In that case we have an object itself as private member. (By the way, calling this entity as object am I write from the terminology point of view?)

Yes you can say "an object" or "an instance" of the class.

You can also talk about including the data member "by value" instead of "by pointer" (because "by pointer" and "by value" is the normal way to talk about passing parameters, therefore I expect people would understand those terms being applied to data members).

Is there any "rules" to decide which one to use? Am I missed something?

If the instance is shared by more than one container, then each container should include it by pointer instead of value; for example if an Employee has a Boss instance, include the Boss by pointer if several Employee instances share the same Boss.

If the lifetime of the data member isn't the same as the lifetime of the container, then include it by pointer: for example if the data member is instantiated after the container, or destroyed before the container, or destroyed-and-recreated during the lifetime of the container, or if it ever makes sense for the data member to be null.

Another time when you must including by pointer (or by reference) instead of by value is when the type of the data member is an abstract base class.

Another reason for including by pointer is that that might allow you to change the implementation of the data member without recompiling the container. For example, if Car and Carburetor were defined in two different DLLs, you might want to include Carburetor by pointer: because then you might be able to change the implementation of the Carburetor by installing a different Carburetor.dll, without rebuilding the Car.dll.

ChrisW
+1 for mentioning references as they seem to have been overlooked so far
Glen
A: 

Generally, the non-pointer version is easier to use and maintain.

But in some cases, you can't use it. For example if the car has multiple carburetors and you wish to put them in an array, and the Carburetor constructor requires an argument: you need to create them via new and thus store them as pointers.

Frederik Slijkerman