calling virtual from constructor

Discussion in 'C++' started by Christopher, Feb 14, 2009.

  1. Christopher

    Christopher Guest

    I am beginnign to get the impression this is a very bad idea. Since I
    have begun deriving classes my program is doing extremely odd things,
    like mysteruiously exiting without ever throwing an exception, or
    jumping to code that it shoudln't.

    Can someone explain what happens when you call a virtual function or a
    pure virtual function from a base class constructor?

    How do you go about rearranging you code such that it does not do
    this, when part of your intialization is going to require calling
    methods of derived classes?
     
    Christopher, Feb 14, 2009
    #1
    1. Advertising

  2. * Christopher:
    > I am beginnign to get the impression this is a very bad idea. Since I
    > have begun deriving classes my program is doing extremely odd things,
    > like mysteruiously exiting without ever throwing an exception, or
    > jumping to code that it shoudln't.
    >
    > Can someone explain what happens when you call a virtual function or a
    > pure virtual function from a base class constructor?


    If you call an ordinary virtual function from a constructor of class T you get a
    virtual call as usual (unless you qualify the call), but the type of the object
    is T at this point, so you get the same effect as with any other T object.

    If you call a pure virtual function you get UB.

    Most compilers will however ensure that that call results in some run time
    error, or if the call is sufficiently direct, detect at compile that you're
    doing it.


    > How do you go about rearranging you code such that it does not do
    > this, when part of your intialization is going to require calling
    > methods of derived classes?


    Oh, the problem of doing derived-class specific initialization.

    It's just a matter of separating concerns in a proper way.

    A few such ways are outlined in the FAQ (I once convinced Marshall to add it),
    but the code below is the way I think is most generally useful:


    <code>
    #include <iostream>
    #include <memory>
    #include <string>

    namespace api {
    struct Widget
    {
    virtual ~Widget() {}
    virtual std::string const typeName() const { return "Widget"; }
    virtual std::string const text() const { return ""; }
    };

    struct Button: Widget
    {
    std::string myText;
    Button( char const aText[] ): myText( aText ) {}
    virtual std::string const typeName() const { return "Button"; }
    virtual std::string const text() const { return myText; }
    };
    } // namespace api

    namespace oo {

    class Widget
    {
    private:
    std::auto_ptr<api::Widget> myWidget;

    Widget( Widget const& );
    Widget& operator=( Widget const& );

    protected:
    api::Widget const& widget() const { return *myWidget; }

    public:
    class Init
    {
    public:
    virtual ~Init() {}
    virtual std::auto_ptr<api::Widget> newWidget() const
    {
    return std::auto_ptr<api::Widget>( new api::Widget );
    }
    };

    Widget( Init const& initializer = Init() )
    : myWidget( initializer.newWidget() )
    {}

    std::string const typeName() const { return myWidget->typeName(); }
    };

    class Button: public Widget
    {
    private:
    Button( Button const& );
    Button& operator=( Button const& );

    public:
    class Init: public Widget::Init
    {
    private:
    std::string myText;
    public:
    Init( std::string const& aText ): myText( aText ) {}

    virtual std::auto_ptr<api::Widget> newWidget() const
    {
    return std::auto_ptr<api::Widget>( new api::Button(
    myText.c_str() ) );
    }
    };

    Button( std::string const& text = "OK" ): Widget( Init( text ) ) {}
    Button( Init const& initializer ): Widget( initializer ) {}

    std::string text() const { return widget().text(); }
    };
    } // namespace oo

    int main()
    {
    using namespace std;

    oo::Button btn;

    cout << btn.typeName() << " \"" << btn.text() << "\"" << endl;
    }
    </code>


    Cheers, & hth.

    - Alf
     
    Alf P. Steinbach, Feb 15, 2009
    #2
    1. Advertising

  3. Christopher

    Greg Herlihy Guest

    On Feb 14, 4:10 pm, "Alf P. Steinbach" <> wrote:
    > * Christopher:
    >
    > > I am beginnign to get the impression this is a very bad idea. Since I
    > > have begun deriving classes my program is doing extremely odd things,
    > > like mysteruiously exiting without ever throwing an exception, or
    > > jumping to code that it shoudln't.

    >
    > > Can someone explain what happens when you call a virtual function or a
    > > pure virtual function from a base class constructor?

    >
    > If you call an ordinary virtual function from a constructor of class T you get a
    > virtual call as usual (unless you qualify the call), but the type of the object
    > is T at this point, so you get the same effect as with any other T object..


    There is a difference between a virtual function call made in T's
    constructor (and destructor) and one made in any other T member
    function. A virtual function call made in T's constructor (to the
    object being constructed) will call either a member function of T or
    one of its base classes. In particular, the call will not execute a
    member function in a T-derived class that overrides the virtual
    function - even if the class of the object being constructed does
    derive from T and does override the virtual function being called.

    So any C++ programmer who expects that a virtual function call made
    from a constructor can be dispatched to a function in a derived class
    - as is usually the case - is mistaken.

    Greg
     
    Greg Herlihy, Feb 15, 2009
    #3
  4. * Greg Herlihy:
    > On Feb 14, 4:10 pm, "Alf P. Steinbach" <> wrote:
    >> * Christopher:
    >>
    >>> I am beginnign to get the impression this is a very bad idea. Since I
    >>> have begun deriving classes my program is doing extremely odd things,
    >>> like mysteruiously exiting without ever throwing an exception, or
    >>> jumping to code that it shoudln't.
    >>> Can someone explain what happens when you call a virtual function or a
    >>> pure virtual function from a base class constructor?

    >> If you call an ordinary virtual function from a constructor of class T you get a
    >> virtual call as usual (unless you qualify the call), but the type of the object
    >> is T at this point, so you get the same effect as with any other T object.

    >
    > There is a difference between a virtual function call made in T's
    > constructor (and destructor) and one made in any other T member
    > function.
    >
    > A virtual function call made in T's constructor (to the
    > object being constructed) will call either a member function of T or
    > one of its base classes. In particular, the call will not execute a
    > member function in a T-derived class that overrides the virtual
    > function - even if the class of the object being constructed does
    > derive from T and does override the virtual function being called.


    Since you're writing this in response to what you quoted it's unclear what you mean.

    One interpretation is that you mean the /same/ as what you quoted, in which case
    it's correct.

    Another interpretation is that you mean something /different/, e.g. that the
    calls you're talking about are source code call specifications, or whatever, in
    which case sorry, then it's /incorrect/.


    > So any C++ programmer who expects that a virtual function call made
    > from a constructor can be dispatched to a function in a derived class
    > - as is usually the case - is mistaken.


    This, however, sounds like you mean the same as what you quoted?


    Cheers & hth.,

    - Alf
     
    Alf P. Steinbach, Feb 15, 2009
    #4
  5. * Daniel T.:
    > Christopher <> wrote:
    >
    >> I am beginnign to get the impression this is a very bad idea. Since I
    >> have begun deriving classes my program is doing extremely odd things,
    >> like mysteruiously exiting without ever throwing an exception, or
    >> jumping to code that it shoudln't.
    >>
    >> Can someone explain what happens when you call a virtual function or a
    >> pure virtual function from a base class constructor?

    >
    > Very bad things.


    Only in the case of calling a pure virtual routine, which is UB.


    >> How do you go about rearranging you code such that it does not do
    >> this, when part of your intialization is going to require calling
    >> methods of derived classes?

    >
    > I've been coding in C++ professionally for 10 years now and have never
    > been in a situation where I thought this would even be helpful.


    The case pops up now and then, in particular when wrapping an existing class
    hierarchy (whether it's a conceptual hierarchy or one already coded in C++).

    GUI programming is one example.

    The FAQ outlines some useful techniques.


    Cheers & hth.,

    - Alf
     
    Alf P. Steinbach, Feb 15, 2009
    #5
  6. Christopher

    Christopher Guest

    On Feb 14, 7:51 pm, "Daniel T." <> wrote:

    > I've been coding in C++ professionally for 10 years now and have never
    > been in a situation where I thought this would even be helpful.


    I've been coding professionally longer than that, and never run across
    this particular situation I'm in.
    I could code for 15 years and never even touch the entry point to an
    application. It all depends on what you are working on and what you
    are supposed to build on top of I guess. I'm sure things just have to
    be redesigned. I wasn't happy with the design to begin with.

    > Know that the derived class constructor will be getting called
    > immediately after the base class constructor is done. Just do whatever
    > you want to the derived class' member-variables there.


    That isn't the situation. It is currently set up such that, as soon as
    the base class is constructed, a windows callback is called
    indirectly, which in turn is calling virtual members to notify them of
    events, when those derived classes haven't even been constructed yet.
    I thought it was fishy.
     
    Christopher, Feb 15, 2009
    #6
  7. Christopher

    James Kanze Guest

    On Feb 15, 2:51 am, "Daniel T." <> wrote:
    > Christopher <> wrote:
    > > I am beginnign to get the impression this is a very bad
    > > idea. Since I have begun deriving classes my program is
    > > doing extremely odd things, like mysteruiously exiting
    > > without ever throwing an exception, or jumping to code that
    > > it shoudln't.


    > > Can someone explain what happens when you call a virtual
    > > function or a pure virtual function from a base class
    > > constructor?


    > Very bad things.


    More or less. If the function isn't pure virtual, the behavior
    is well defined. Whether it is what you want or not is a
    different issue.

    > > How do you go about rearranging you code such that it does
    > > not do this, when part of your intialization is going to
    > > require calling methods of derived classes?


    > I've been coding in C++ professionally for 10 years now and
    > have never been in a situation where I thought this would even
    > be helpful.


    > Know that the derived class constructor will be getting called
    > immediately after the base class constructor is done. Just do
    > whatever you want to the derived class' member-variables
    > there.


    I don't think that's the point. The most frequent case I've
    seen where this occurs is when the template method pattern is
    used. The answer is, as Alf said, to use the strategy pattern
    instead. (One trick here is to derive from the base class,
    making the concrete strategy---the delegate---a private base of
    the derived class, e.g.:

    class Base
    {
    protected:
    Base( Strategy* strategy ) ;
    } ;

    class Derived : private DerivedStrategy, public Base
    {
    Derived() : Base( this ) {}
    } ;

    Note that because the DerivedStrategy is listed first, it will
    be constructed before the Base.

    (The actual code must be a bit more complicated than that; the
    implicit conversion in the base initializer is undefined
    behavior, and I've actually encountered one compiler where it
    doesn't work. So I wrap the DerivedStrategy in a simple class
    which has a member function to return the pointer to the
    strategy.)

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Feb 15, 2009
    #7
  8. Alf P. Steinbach wrote:
    > If you call a pure virtual function you get UB.


    Is that so even if the pure virtual function had an implementation in
    the base class?
     
    Juha Nieminen, Feb 15, 2009
    #8
  9. * Juha Nieminen:
    > Alf P. Steinbach wrote:
    >> If you call a pure virtual function you get UB.

    >
    > Is that so even if the pure virtual function had an implementation in
    > the base class?


    If it is pure in class T and the call originates from a T constructor, yes.

    This holds even if it has an implementation in class T, and the call is unqualified.

    It can have an implementation and still be declared as pure (that is, "= 0").


    Cheeers,

    - Alf
     
    Alf P. Steinbach, Feb 15, 2009
    #9
  10. Christopher

    James Kanze Guest

    On Feb 15, 6:04 pm, Juha Nieminen <> wrote:
    > Alf P. Steinbach wrote:
    > > If you call a pure virtual function you get UB.


    > Is that so even if the pure virtual function had an
    > implementation in the base class?


    If the call is virtual, yes. Basically, if dynamic name
    resolution resolves to a pure virtual function, the behavior is
    undefined. You can use a qualified name to call it, however,
    and that is well defined.

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Feb 16, 2009
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Giulio
    Replies:
    9
    Views:
    1,047
    Patrick Kowalzick
    Jun 25, 2003
  2. Brett Irving
    Replies:
    3
    Views:
    3,337
    John Harrison
    Jun 29, 2003
  3. lallous
    Replies:
    5
    Views:
    8,828
    David Harmon
    Jan 23, 2004
  4. Asfand Yar Qazi
    Replies:
    6
    Views:
    15,640
    jeffc
    May 17, 2004
  5. Generic Usenet Account
    Replies:
    10
    Views:
    2,248
Loading...

Share This Page