6

I need to call a virtual method for all classes derived from a given base base class right after the construction of the derived object. But doing so in the base class constructor will result in a pure virtual method call

Here is a simplified example:

struct Loader {
    int get(int index) { return 0; }
};

struct Base{
    Base() {
        Loader l; 
        load( l ); // <-- pure virtual call!
    }
    virtual void load( Loader & ) = 0;
};

struct Derived: public Base {
    int value;
    void load( Loader &l ) {
        value = Loader.get(0);
    }
};

I can call load at the Derived constructor, but Derived could not know how to create a Loader. Any ideas/workarounds?

Vargas
  • 2,125
  • 2
  • 33
  • 53
  • What's the problem? You can call a pure virtual method. – Benoit Thiery Oct 20 '10 at 19:55
  • 2
    @Benoit: Not in a constructor. @Vargas: It can probably be designed better, so you don't have this dependency. For example, why is `load` a separate function that's being called in the constructor? Why not let `Derived` load its own values. – GManNickG Oct 20 '10 at 19:56
  • @Benoit: From the constructor?!!! That's called undefined behavior in C++ – Armen Tsirunyan Oct 20 '10 at 19:56
  • 1
    When you need to first call the constructor and then some init function, that's called two-phase construction and is best hidden behind an interface, because it's very error prone for users of your class. – sbi Oct 20 '10 at 20:01
  • @sbi: thats exactly this error prone behavior that I'm trying to avoid – Vargas Oct 20 '10 at 20:15
  • 2
    Can you please put more context into the question? *What* are you trying to do? Not how. – GManNickG Oct 20 '10 at 20:25
  • 1
    Use the factory pattern. – Hans Passant Oct 20 '10 at 20:56
  • 1
    Could you not use composition rather than inheritance? If there is no pressing need to use inheritance, composition would make this trivial. – Nim Oct 20 '10 at 21:58

8 Answers8

7

The problem is that base class construction occurs before the derived class is fully constructed. You should either call "load" from the derived class, initialise throguh a different virtual member function or create a helper function to do this:

Base* CreateDerived()
{
    Base* pRet = new Derived;
    pRet->Load();
    return pRet;
}
Goz
  • 61,365
  • 24
  • 124
  • 204
  • Ok, I managed to get this far, but how can I prevent Derived from being created elsewere, and therefore being used without proper initialization? – Vargas Oct 20 '10 at 20:02
  • @Vargas: For every class derived from Base make the constructor private and define a `Base* CreateDerivedX()` and make it a friend. – Eugen Constantin Dinca Oct 20 '10 at 20:08
  • @Eugen, can this be done without needing to change every derived class? – Vargas Oct 20 '10 at 20:11
  • @Vargas: No, the only thing you can simplify is having a templated CreateDerivedX. The rest (private ctor & friend CreateDerivedX) you need to do for every class derived from Base. – Eugen Constantin Dinca Oct 20 '10 at 20:41
  • @Goz: As it is when I'm writing this, the above code leaks memory (and possibly other resources) if `Load` throws an exception. It's possible to fix that particular problem, but that problem is only one -- typical -- manifestation of the sheer brittleness of two-phase construction. Don't use two-phase construction when it's easy to avoid, as it is in most cases including this. Think of two-phase as a very sexy woman with a host of veneral diseases. Just say "no" to her, however alluring she may be. Cheers, – Cheers and hth. - Alf Oct 20 '10 at 23:11
  • Make CreateDerived a public static member function of Derived, make the constructors of Derived protected or private. Creating one will look like this `Derived * d = Derived::create()` and trying to create one using `Derived * d = new Derived;` wont be possible due to the private constructors. – Michael Anderson Oct 21 '10 at 01:53
3

The C++ FAQ calls this problem DBDI, Dynamic Binding During Construction. Mainly, the problem is to avoid the Evil two-phase construction advocated in other answers here. It's sort of "my" FAQ item -- I convinced Marshall to add it.

However, Marshall's take it on it is very general (which is good for a FAQ), while I was more concerned with the particular design/coding pattern.

So, instead of sending you to the FAQ I send you to my own blog, the article "How to avoid post-construction by using Parts Factories", which links to the relevant FAQ item, but discusses in depth the pattern.

You can just skip the first two paragraphs...

I sort of rambled there. :-)

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
2

Use the PIMPL pattern:

template<typename T>
class Pimpl
{
    public:
        Pimpl()
        {
            // At this point the object you have created is fully constructed.
            // So now you can call the virtual method on it.
            object.load();
        }
        T* operator->()
        {
            // Use the pointer notation to get access to your object
            // and its members.
            return &object;
        }
    private:
        T    object;   // Not technically a pointer
                       // But otherwise the pattern is the same.
                       // Modify to your needs.
};

int main()
{
    Pimpl<Derived>   x;
    x->doStuff();
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
1

Can't you add a method getLoader() in your Base class so that DerivedClass constructor can call it on this to get a Loader ? As DerivedClass constructor will be called after Base class constructor, that should work fine.

Benoit Thiery
  • 6,325
  • 4
  • 22
  • 28
1

Its hard to give advice unless you tell us what you are trying to accomplish, rather than how. I find that its usually better to construct such objects from a factory, which will load the required data before-hand, and then pass the data into the constructor of the object.

Kent Murra
  • 234
  • 1
  • 2
0

Many known frameworks (like MFC) do this: They make a (virtual) member-function Init() or Create() and do the initialization there and then mandate in the documentation that the user call it. I know you won't like this idea, but you just can't call a virtual method from a constructor and expect it to behave polymorphically, regardless of the methods pureness...

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
0

There are many ways to correct this, here is 1 suggestion fitting within your provided framework

struct Loader {
    int get(int index) { return 0; }
};

struct Base{
    Base() {
    }
    Loader & getLoader( );
private:
    Loader l; 
};

struct Derived: public Base {
    int value;
    Derived( ) {
        value = getLoader().get(0);
    }
};
Greg Domjan
  • 13,943
  • 6
  • 43
  • 59
  • Note: your `Derived` constructor is not properly declared. Also, if the `Loader` instance is expensive and should not be alive for too long (i.e. reads from an XML or JSON file), this will be a problem. However, thumbs up for a backward compatible solution! – André Caron Oct 20 '10 at 22:43
  • Certainly the lifetime of the Loader object changes, you raise a good point on potential the associated cost of that. – Greg Domjan Oct 21 '10 at 01:54
0

This may come a little late after other answers, but I'll still give it a try.

You can implement this safely, and without changing derived classes. However, you will need to change use of all these classes, which might be far worse, depending on your scenario. If you are still designing, then this might be viable alternative.

Basically, you can apply the curiously recurring template pattern and inject the initialization code after the constructor gets invoked. Furthermore, if you do it as I've written it below, you can even protect load from being called twice.

struct Loader {
    int get(int index) { return 0; }
};
struct Base {
    virtual ~Base() {} // Note: don't forget this.
protected:
    virtual void load( Loader & ) = 0;
};

struct Derived : public Base {
    int value;
protected:
    void load( Loader &l ) {
        value = l.get(0);
    }
};

template<typename T>
class Loaded : public T
{
public:
    Loaded () {
        Loader l; T::load(l);
    }
};

int main ( int, char ** )
{
    Loaded<Derived> derived;
}

Frankly, though, I would consider an alternate design if you can. Move the code from load to your constructors and provide the loader as an a reference argument defaulting as follows:

struct Derived : public Base {
     Derived ( Loader& loader = Loader() ) { ... }
}; 

That way, you completely avoid the problem.

Summary: your choices are the following:

  1. If you are not limited by external constraints and don't have an extensive code base depending on this, change your design for something safer.
  2. If you want to keep load the way it is and not change your classes too much but are willing to pay the price of changing all instantiations, apply CRTP as proposed above.
  3. If you insist on being mostly backward compatible with existing client code, you will have to change you classes to use a PIMPL as others have suggested or live with the existing problem.
André Caron
  • 44,541
  • 12
  • 67
  • 125
  • Also, if you want to make sure objects are not created without being loaded, declare constructors as protected. This, however, is *not* fail-safe because other clients can simply derive from your class and do whatever they want, including not call `load` or call it twice. – André Caron Oct 20 '10 at 22:40
  • é: first just a nit: the 2nd example lacks a `const` (it's invalid code as presented). I agree with your concern about getting rid of 2-phase construction. For a more general approach see my earlier answer. And/or the C++ FAQ. Cheers & hth., – Cheers and hth. - Alf Oct 20 '10 at 23:19
  • @Alf: I left the `const` out because in the problem statement, `Loader::get()` was not `const`. But you're right, it is invalid syntax and I should have put it anyways. As for your post, I also checker your blog. I've used the exact same design in the exact same type of library, so I totally agree. I posted these two alternatives because I find they take up less code to write. – André Caron Oct 20 '10 at 23:32