The simplest solution for this exact case is just to pass the id as an
argument to Base
, and be done with it. As long as it's just a
question of data, no virtual function is needed. In more complicated
cases, you could pass a pointer to a struct
, or even the address of a
(static member or free) function.
In more complicated cases, the strategy pattern may apply: the actual
customization is delegated to a separate hierarchy, and the derived
class constructor passes a pointer to a derived delegate. If the
delegate has no state, it can be a static instance somewhere.
Finally, if all else fails, you can use a dummy argument (if the derived
has no arguments) or wrap an argument. This requires some collaboration
from the derived class, and doesn't work well with temporaries, but I've
used it successfully once or twice. Basically, you define something
like:
class Base
{
public:
class DeferredInit
{
friend class Base;
mutable Base* myOwner;
public:
DeferredInit() : myOwner( NULL ) {}
~DeferredInit()
{
if ( myOwner != NULL ) {
myOwner->postCtor();
}
}
};
Base( DeferredInit const& initializer )
{
initializer.myOwner = this;
}
};
Derived classes are then something like:
class Derived : public Base
{
public:
Derived( Base::DeferredInit const& fromAbove = Base::DeferredInit() )
: Base( fromAbove )
{
}
};
The one time I used this, all of the classes accepted an std::string
as input, so I arranged for DeferredInit
to convert implicitly from
std::string
and char const*
, wrapping the argument. Again, the
client code could just write:
Derived d( "some string" );
and postCtor
was called at the end of the full expression. (That's
why things like:
Derived( "abc" ).doSomething();
don't work. You must declare an instance to be sure that postCtor
is
called before using the object in any other way. No temporaries!)
But I'd only consider this solution as a last resort. It introduces
additional complexity, and adds the restriction concerning temporaries.