views:

83

answers:

3

So I'm using composition to bring together a collection of objects, all of which are derived from a base class, lets say Component. E.g:

class Component {
public:
  Component();
  ...
private:
  int m_address;
  ...
};

class SpecializedComponent: public Component {
public:
  SpecializedComponent()
  ... //and so on
};

class SpecializedComponent2: public Component {
public:
  SpecialIzedComponent2()
  ... //and so on
};

class ComponentHolder{
  SpecializedComponent* m_descriptiveName;
  SpecializedComponent2* m_descriptiveName2;
  // and so on... many different types of components
}

So, each SpecializedComponentX will communicate over a network with an individual data source, each with their own unique address. These addresses are specified in a parameter file. At the moment, I'm parsing the parameter file and the m_address is initialized in the derived class constructor - this is because each m_address is specified by the type of object we're initializing.

Every SpecializedComponentX has some common functionality that I want to execute in the base class Component. So, I spin up a thread associated with the base class Component, right? Sure - makes sense. Until I realize that I don't have the destination address for that component yet - because the object hasn't been fully constructed. I want to spin up the base class thread in the ctor, but I don't know the m_address yet.

The only way I can think of getting around this is providing a (simple) virtual function, void start(), that the derived class can call to spin up the thread once the object is fully constructed. Is this a valid and appropriate design choice or is there a pattern that I could be overlooking? Thanks.

A: 

Component could have a constructor with one argument that initializes m_address.

Hans Passant
yes, of course. sometimes the obvious thing to do is the right thing.
jdt141
A: 

If this common functionality depends in any way on the state of SpecializedComponent, SpecializedComponent2, or on the fact that this is a SpecializedComponent, or SpecializedComponent2, then you cannot really do it in the constructor of Component, unless you pass in parameters to it. It is sometimes a necessary evil to pass in a type-identifier to the Component constructor to do such initialization there.

In this case, however, you could create a virtual function that the derived class can call. However, suppose you place this virtual function call in the SpecializedComponent constructor. If you later derive a further class down from this (SuperSpecializedComponent) and ovrerride this virtual function, the call you make from SpecialzedComponent constructor will not even hit that. Moral: don't call virtual functions from a constructor.

I think the cleanest way would be to have a 2-phase construction. A constructor that does basic wiring up of the object and an Init() method that must be called before you use it. The client code (ComponentHolder?) could call this Init after all objects are fully constructed.

Tarydon
A: 

Why does the start() method need to be virtual?

You could make it non-virtual and implement your derived constructors like this:

SpecializedComponent::SpecializedComponent() {
    m_address = getAddressFromParameterFile("SpecializedComponent");
    start();
}

This is preferable to calling start() after the object is fully constructed, since two-step construction is error-prone. (If you do need two-step construction, consider a factory (or factory method) to assure only fully constructed objects exist. Make the constructor private and the factory (method) a friend.)

Another way would be to move the calculation of the address out of the object construction. This would result in code like this:

SpecializedComponent::SpecializedComponent(const std::string& address)
 : Component(address)
{}

Component::Component(const std::string& address)
 : m_address(address)
{
    start();
}

This approach increases testability of the derived SpecializedComponents, because it removes the dependency on the parameter file.

For convenience, you could provide a static factory method to instance your SpecializedComponents:

SpecializedComponent* SpecializedComponent::create() {
    std::string address = getAddressFromParameterFile("SpecializedComponent");
    return new SpecializedComponent(address);
}

BTW: consider holding shared_ptr's in ComponentHolder, instead of raw pointers.

Ralph