How can I get polymorphic behavior in a C++ constructor?
Asked Answered
C

4

8

I have a base class that I want to look like this:

class B
{
    // should look like: int I() { return someConst; }
    virtual int I() = 0;
    public B() { something(I()); }
}

The point being to force deriving classes to override I and force it to be called when each object is constructed. This gets used to do some bookkeeping and I need to know what type of object is being constructed (but I otherwise treat the current object as the base class).

This doesn't work because C++ won't let you call an abstract virtual function from the constructor.

Is there a way to get the same effect?


Based on this link it would seem that the answer is there is no way to get what I want. However what it says is:

The short answer is: no. A base class doesn't know anything about what class it's derived from—and it's a good thing, too. [...] That is, the object doesn't officially become an instance of Derived1 until the constructor Derived1::Derived1 begins.

However in my case I don't want to know what it is but what it will become. In fact, I don't even care what I get back as long as I the user can (after the fact) map it to a class. So I could even use something like a return pointer and get away with it.

(now back to reading that link)

Cunctation answered 21/9, 2009 at 6:42 Comment(1)
Read this: https://mcmap.net/q/1468519/-safe-way-to-initialize-a-derived-class. You may be able to use PIMPL.Derogative
L
8

You can't call virtual methods from the constructor (or to be more precise, you can call them, but you'll end up calling the member function from the class currently being constructed)., the problem is that the derived object does not yet exist at that moment. There is very little you can do about it, calling virtual methods from the constructor polymorphically is simply out of the question.

You should rethink your design -- passing the constant as an argument to the constructor, for example.

class B
{
public:
    explicit B(int i)
    {
        something(i);
    }
};

See C++ faq for more. If you really want to call virtual functions during construction, read this.

Leopardi answered 21/9, 2009 at 6:48 Comment(5)
Having an init() method you call after the constructor is a workaround. But then you need to change everywhere the class is constructed - which is fine if you're using a factory.Fleeting
The C++ FAQ entry is good, and it is also a good idea to read the corresponding FQA entry: yosefk.com/c++fqa/inheritance-mother.html#fqa-23.6Frambesia
You can call virtual methods in constructors. They just don't do what you expect them to do.Becki
You mean the FQA entry that says "How about trying another programming language?"?Leopardi
This is what I ended up doing, fortunately, I could set things up so that only one base class constructor is ever exposed and it is only ever called from derived class constructors. (and thanks to predefined macros, always by the exact same form: Base(__SOMEMACRO__))Cunctation
T
1

Perhaps use a static factory method on each derived type? This is the usual way to construct exotic objects (read: those with very specific initialisation requirements) in .NET, which I have come to appreciate.

class Base
{
  protected Base(int i)
  {
    // do stuff with i
  }
}

class Derived : public Base
{
  private Derived(int i)
    : Base(i)
  {
  }

  public Derived Create()
  {
    return new Derived(someConstantForThisDerivedType);
  }
}

Calling virtual methods in base constructors is generally frowned upon, as you can never be certain of a particular method's behaviour, and (as somebody else already pointed out) derived constructors will not have yet been called.

Truth answered 21/9, 2009 at 6:58 Comment(0)
W
0

That will not work as the derived class does not yet exist when the base class constructor is executed:

class Base
{
public:
    Base()
    {
        // Will call Base::I and not Derived::I because
        // Derived does not yet exist.
        something(I());
    }

    virtual ~Base() = 0
    {
    }

    virtual int I() const = 0;
};

class Derived : public Base
{
public:
    Derived()
     : Base()
    {
    }

    virtual ~Derived()
    {
    }

    virtual int I() const
    {
        return 42;
    }
};

Instead you could add the arguments to the base class constructor:

class Base
{
public:
    explicit Base(int i)
    {
        something(i);
    }

    virtual ~Base() = 0
    {
    }
};

class Derived : public Base
{
public:
    Derived()
     : Base(42)
    {
    }

    virtual ~Derived()
    {
    }
};

Or if you're really fond of OOP you could also create a couple of additional classes:

class Base
{
public:
    class BaseConstructorArgs
    {
    public:
        virtual ~BaseConstructorArgs() = 0
        {
        }

        virtual int I() const = 0;
    };

    explicit Base(const BaseConstructorArgs& args)
    {
        something(args.I());
    }

    virtual ~Base() = 0
    {
    }
};

class Derived : public Base
{
public:
    class DerivedConstructorArgs : public BaseConstructorArgs
    {
    public:
        virtual ~DerivedConstructorArgs()
        {
        }

        virtual int I() const
        {
            return 42;
        }
    };

    Derived()
     : Base(DerivedConstructorArgs())
    {
    }

    virtual ~Derived()
    {
    }
};
Woolfolk answered 21/9, 2009 at 7:11 Comment(0)
M
0

What you need is two-phase construction. Use the Universal Programmer's cure: Add another layer of indirection.

Miocene answered 21/9, 2009 at 8:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.