0

I'm trying to understand how method overrides work when the to-be-overridden method is called from a constructor.

(Using VS2012)

Given the test program:

#include <string>
#include <iostream>

using namespace std;

struct Foo
{
    Foo()
    {
        cout << "\tFoo::Foo()" << endl;
        Initialize();
    }

    virtual void Initialize()
    {
        cout << "\tFoo::Initialize()" << endl;
    }

};

struct Bar : public Foo
{
    Bar()  { 
        cout << "\tBar::Bar()" << endl; 
    } 

    void Initialize() override
    {
        cout << "\tBar::Initialize()" << endl;
    }
};


int main(int argc, char *argv[])
{
    cout << "Creating Foo" << endl;

    Foo foo;

    cout << endl;

    cout << "Creating Bar" << endl;

    Bar bar;

    std::getchar();

    return 0;
}

I get the following output:

Creating Foo
        Foo::Foo()
        Foo::Initialize()

Creating Bar
        Foo::Foo()
        Foo::Initialize()
        Bar::Bar()

I would expect the second section to be:

Creating Bar
    Foo::Foo()
    Foo::Initialize()
    Bar::Initialize()   <---- Not called.
    Bar::Bar()

I'd expect that, when the Foo ctor is called, and it calls ::Initialize(), that the override Bar::Initialize() is actually called.

Now, I can guess why this is happening: When Foo::Foo() is called, before the body of Bar::Bar() is executed, there's no guarantee that items needed by Bar::Initialize() have been allocated, much less initialized.

The Question

Can I override a method that is called by a base constructor, and have that override execute when instantiating a derived class? That is, is there any way to modify the above code so that Bar::Initialize() will be called?

The alternative I'm using at the moment is to remove the ::Initialize() from the Foo constructor, and do something like this:

Bar bar;
bar.Initialize();

and modify Bar::Initialize() to

void Bar::Initialize() override {
Foo::Initialize();
///... bar-specific initialization here
}

Is this the best or only way to do this?

3Dave
  • 28,657
  • 18
  • 88
  • 151

1 Answers1

4

Sorry, you can't do this because while the Foo constructor is running, you have a Foo and not a Bar. None of Bar's overrides are available until the Bar constructor runs.

The level of control the subclass has during superclass construction is which superclass constructor gets called and what parameters it gets, via the initializer mechanism.

BTW, if it worked like you expected it to, your output would actually be:

Creating Bar
    Foo::Foo()
    Bar::Initialize()
    Bar::Bar()
Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
  • That's what I expected. So, that means that I'm stuck with the separate instantiation-followed-by-Initialize() call every time I need one of these things? (It's hardly that much of an inconvenience, but seems a lot like repeating myself all over the place.) – 3Dave Jan 26 '14 at 20:25
  • I'm not sure you need `Initialize` at all. If you're simply calling `Initialize` at the end of the `Foo` constructor, just put that code in the `Bar` constructor. – Mike DeSimone Jan 26 '14 at 20:27
  • That makes sense. The circumstances that led to this pattern are a bit odd; guess it's time for some refactoring. – 3Dave Jan 26 '14 at 20:27
  • The real question is why you feel the need to do this... – Mike DeSimone Jan 26 '14 at 20:27
  • There are multiple constructors, each of which performs some identical initialization. Rather than having to explicitly call ::Initialize() in each derived class (which is where I put the initialization code, rather than cut & paste in the each constructor), I thought I'd call it from base constructor. Like I said, it makes sense why that won't work - I was just wondering if there was a better alternative. (Note that VS2012, which I'm using, doesn't support constructor chaining, unfortunately.) – 3Dave Jan 27 '14 at 00:40
  • If there is code that is the same in every class, it should be in a method in `Foo` and be called from the appropriate constructors; see http://stackoverflow.com/questions/7349183/constructor-chaining-in-c. My condolences regarding VS2012. – Mike DeSimone Jan 27 '14 at 02:57