Re: Huh? Using virtual base class semantics to your advantage
Rex Kerr wrote:
In the book C++ Coding Standards (Sutter & Alexandrescu), in
item #49 (Avoid calling virtual functions in constructors and
destructors), there is a bullet point that says that one
method of ensuring post-construction is to:
Use Virtual Base Class Semantics: Language rules dictate that
the constructor most-derived class decides which base
constructor will be invoked; you can use that to your
advantage.
First: I assume that he meant "of the most-derived..."
Correct.
Second: I understand what he's getting at as far as virtual
base classes, and the need to explicitly call the desired
constructor from within the most derived constructor. But I
just can't think of any way to use this to ensure proper post
construction! I don't have easy access to the reference that
he cites [Taligent94], so I can't look there right now.
The reference (at least the one cited by other responders)
explains a way of determining in a constructor or a destructor
whether you are the most derived class or not, and calling the
function only if you are. As far as I can see, it doesn't
provide a means of avoiding having to write the call in every
derived class. (I rather question the utility of the trick. I
can think of very few cases---the template method pattern where
you provide a default, for example---where you don't know
whether you are the most derived class or not.)
One trick I've used in the past for this is a dummy parameter
type. If your constructor takes a parameter, you wrap it with
in an initializer class with an implicit conversion constructor;
if it doesn't, you provide an initializer class with a default
constructor. In both cases, the base class constructor takes a
(const) reference to the initializer class. Something like:
class Init ;
class InitWithParam ;
class Target
{
public:
Target( Init const& = Init() ) ;
Target( InitWithParam const& param ) ;
virtual void initialize() = 0 ;
} ;
class Init
{
friend class Target ;
public:
Init()
: myOwner( NULL )
{
}
~Init()
{
if ( myOwner != NULL ) {
myOwner->initialize() ;
}
}
private:
mutable Target* myOwner ;
} ;
class InitWithParam : public Init
{
friend class Target ;
public:
InitWithParam( std::string const& param )
: myParam( param )
{
}
private:
std::string const& myParam ;
} ;
Target::Target(
Init const& init )
{
// ...
init.myOwner = this ;
}
Target::Target(
InitWithParam const&init )
: myValue( init.myParam )
{
init.myOwner = this ;
}
Obviously, the derived classes have to be aware of this. But
they can't forget it; they must invoke the base class
constructor with an Init. And the only effect on them is to
change the types of the parameters to their constructors.
Note that this technique depends on the destructor of a
temporary being called at the end of the full expression; some
compilers do not do this by default, in order to avoid breaking
pre-standard code. Also, this may not work if the class derived
from Target is a temporary, used something like:
DerivedFromTarget().doSomething() ;
In this case, doSomething will be called before initialize.
--
James Kanze kanze.james@neuf.fr
Conseils en informatique orient?e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S?mard, 78210 St.-Cyr-l'?cole, France +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]