23

The C++ standard says that invoking a pure virtual function from a constructor or destructor is forbidden. What is the reason for this? Why should the standard place a restriction like this?

2
  • 7
    It does not say forbidden. It says undefined. The reason is to allow the compilers the most flexibility possible. Commented Dec 28, 2011 at 5:09
  • Likely to result in PURE_CALL error
    – bobobobo
    Commented Feb 12 at 21:46

6 Answers 6

26

At the point a class destructor is run, all subclass destructors have already been run. It would not be valid to call a virtual method defined by a subclass, for which its destructor has already run.

A similar restriction exists around calling virtual methods in constructors. You can't call a virtual method for a subclass whose constructor has not yet run.

16
  • 3
    My comments about virtual functions also apply to pure virtual functions. Commented Dec 28, 2011 at 4:58
  • 5
    @eharvest: That's not true, a pure virtual function can be called from any non-constructor/destructor method. Subclasses must provide the implementation. Commented Dec 28, 2011 at 5:06
  • 4
    @eharvest: When a virtual function is called from a base class, the actual function called depends on the type of the instance on which the function is called. It does not depend on how many subclasses there are. Commented Dec 28, 2011 at 5:22
  • 4
    There are plenty of undefined things you can do in C++ that the compiler can't detect. Commented Dec 28, 2011 at 5:33
  • 3
    You can call virtual methods from both constructor and destructor and this has well defined semantics in C++: 'the function called is the final overrider in the constructor’s or destructor’s class and not one overriding it in a more-derived class'. And this is the reason why calling pure virtual functions results in undefined behavior. Commented Dec 28, 2011 at 5:57
6

It's the same reason you can't live in a house while you're pouring the foundation or ripping it up. Until the constructor has completed, the object is only partially constructed. And once the destructor starts, the object is partially destroyed. A pure virtual function can only be called on an object that is in a normal state as otherwise the structures needed to figure out which function implementation to call may not exist.

1
  • 2
    And this differs from a non-pure virtual function... how? Commented Feb 8, 2012 at 0:21
4

The C++ standard says, Invoking a pure virtual function from a constructor or destructor is forbidden. What is the reason for this? Why should the standard place a restriction like this?

From an admittedly old draft C++ Standard, but the relevant distinctions I'll draw remain relevant:

10.4-6 Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (class.virtual) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.

This is subtly different to what you're asserting, as the context from the pre-semicolon phrase is implicitly relevant to the post-. Rephrasing:

undefined behaviour happens when an abstract class's constructor or destructor calls one of its own member functions that is (still) pure virtual.

I include the qualifier "(still)" as pure virtual functions are given definitions - and cease to be "pure" - at some point in a class hierarchy. This is a bit weird, but consider this part of the Standard:

A class is abstract if it has at least one pure virtual function. [Note: such a function might be inherited: see below. ]

Clearly, derived classes with definitions for the function that was pure virtual in the base are not necessarily themselves abstract. The above statement can only be true if the sense of "purity" applies not universally to the virtual function itself, but to the levels of class hierarchy in which it remains pure.

The consequence: if the function - from the perspective of the calling constructor/destructor's level in the hierarchy - has already been defined it can be called with well defined behaviour.

With this understanding of what's undefined in the Standard, we can return to your questions: "What is the reason for this? Why should the Standard place a restriction like this?"

The crux of the reason is that a base class must be completely constructed before a derived class contructor kicks in, as the derived class's construction may operate on the base object. Similarly, a base destructor must run after the derived destructor because the latter may still wish to access the base object. Given this necessary ordering, the compiler can't safely dispatch to the derived class virtual function given either the derived class's constructor hasn't yet run to establish the derived class member state, or the derived class's destructor has already invoked destructors for the additional data members and again the object state is no longer guaranteed usable.

For non-pure virtual functions, the compiler can and does fall back on invoking the most specialised definition of the function known for the most-derived class in the hierarchy that's already been constructed and not yet been destructed. But pure virtual functions by definition are those in which no implementation has yet been specified, and at the level in the class hierarchy where the functions are pure no implementation exists to call.

A typical implementation of a virtual dispatch mechanism may represent this by having a pointer in a virtual dispatch table for the base class containing a pure virtual function that is set to 0, uninitialised, or pointed at some "raise an alert" function. As successive layers of derived class constructors kick in, the pointer to the virtual dispatch table will be overwritten with the address of their own VDTs. Those derived classes overriding the implementation will point at their own function defintion, which will become the default for any more-derived classes that don't themselves specify a new implementation. This crucial implicit pointer-to-VDT member will move backwards through this same list of VDTs as the derived class destructors complete, ensuring any virtual calls are to functions on undestructed layers in the class hierarchy. But, when the destructor for the first class in which the virtual function was defined has run, future VDTs will again lack any reference to an actual implementation.

2

Recall that invoking a "non-pure" virtual function from a constructor/destructor ignores the fact that the function is virtual, and always calls the implementation in your class, not in the derived class being constructed. That's why you cannot call pure virtual from constructor or destructor: as far as they are concerned, your pure virtual function has no implementation.

0

the function is only a prototyope to be implemented in subclasses, it does not actually exists in the class... so it can not be called neither in constructor or destructor).

there is no implementation of the function so, simply, there is no code to call :)

the subclasses that implementsthe pure virtual does not exist when calling constructor/destructor.

0

Assuming that a typical pure virtual function has no explicit implementation within its abstract base class. Accordingly:

class Base
{
    public:
    virtual void area() = 0; 
    Base() { area();  }; 
    ~Base() { area(); };
};
void Base::area(){ print("Base::area()"); }

class Derived: public Base
{
    public:
    void area() override { print("Derived::area()"); };
    Derived() {  };
    ~Derived() {  };
};

int main() {
    Derived d;
}

Once you have instantiated the object d, the control of execution will go inside the constructor Base::Base(), and execute its body, and hits area(), and since there's no implementation for area() have been met by the compiler yet, the compiler will issue a warning/error, because Derived (which contain the overridden version of area()) actually has not been created yet. Since the compiler knows that (according to Standard) the pure virtual function must be implemented in all derived classes, whereas those derived classes have not been created by the compiler; the compiler at this moment doesn't know where it should go. Thus when your compiler is stuck, undefined behavior arises.

Not the answer you're looking for? Browse other questions tagged or ask your own question.