tags:

views:

325

answers:

5

I find this strange. In the ctor of Sample_Base, I call bar() which internally calls fun() which is a pure virtual function. I get the error "pure virtual function" called. Which is fine. Now, if I call fun() directly from Sample_Base's ctor, I don't get that error. I tried it on VC++ 2010 Beta 2 and on g++ 4.4.1 on Ubuntu 9.10. I agree that giving an implementation for pure virtual function, other than pure virtual destructor, is meaningless. But, I am a bit surprised about this behaviour.

class Sample_Base
{
public:
    Sample_Base()
    {
        bar();
       // fun();
    }
    /* This is code does not throw any error.
    Sample_Base()
    {
       fun();
    }
    */

    void bar()
    {
        fun();
    }
    virtual void fun() = 0;
    virtual ~Sample_Base();
};

Sample_Base::~Sample_Base()
{

}

void Sample_Base::fun()
{
    std::cout << "Sample_Base::fun\n";
}

class Sample_Derived : public Sample_Base
{
public:
    Sample_Derived() : Sample_Base()
    {
        fun();
    }

    void fun()
    {
        std::cout << "Sample_Derived::fun\n";
    }

    ~Sample_Derived()
    {

    }
};
+6  A: 

Calling a virtual function won't call the overriding functions in the derived classes. Calling a pure virtual function in a constructor or destructor is Undefined behavior.

You might be interested in reading this and this.

Prasoon Saurav
+1  A: 

At construction time, when the Sample_Base constructor is called, the object is not fully constructed yet. Specifically the parts belonging to Sample_Derived are not yet created calls to virtual functions that would be overwritten by Sample_Derived will not call the implementation in Sample_Derived, but the version defined in Sample_Base. And since the function has no implementation there you get an error.

For more information and possible workarounds also see this entry in the C++ FAQ Lite.

sth
I agree that the object is not fully constructed when bar is being called in Sample_Base, but when I call fun() from Sample_Base's constructor, I dont' get any error. That is what I am interested in.
Jagannath
@Jagannath: Receiving or not receiving the error is not what you should worry about, you should be worried about the fact that this design is dangerous. You should try avoiding calling virtual methods from your constructor/destructor.
Gal Goldman
@Gal: I know the design is not correct and it should be avoided to call virtual function from a constructor. I am just interested in the difference in the behaviour. Thanks anyways.
Jagannath
@Jagannath: In that case, Gorpik's explanation is what you're looking for.
Gal Goldman
The behaviour is explicitly _undefined_ which means that you can't rely on the program doing anything that you might expect it to and you can't rely on getting a warning or error.
Charles Bailey
+5  A: 

Providing a definition for a pure virtual function is not necessarily meaningless. Marking a virtual function pure means that the enclosing class is abstract and that any class that derives from it is abstract unless the final override for that function is not a pure virtual function. A pure virtual function can still be called via an explicit non-virtual call.

In the body of a base class constructor (but not from a ctor-initializer) the version of a virtual function called through a virtual call is one defined in the class itself or one of its bases and not of any class overriding it (which would not yet have been constructed). This is specified explicitly in 12.7 [class.cdtor]/3.

It is legal to call a pure virtual function explicitly in a constructor body (i.e. using an explicit class qualifier) - although this would required the function to have a body defined - but it is undefined behaviour to call a pure virtual function via a virtual call which is only possible from the constructor or destructor of an abstract class. This is specified explicitly in 10.4 [class.abstract]/6.

Charles Bailey
+5  A: 

When you call the function directly, since you are in the constructor, the compiler resolves the static type of your object (Sample_Base) and calls Sample_Base::fun() directly. Since you provided an implementation for it, the compiler finds the function and it works.

When you call it indirectly, through bar(), the compiler must use the dynamic type, so it does a virtual call that gets resolved at runtime. And there it fails, because it calls a pure virtual function.

So the difference is in the moment it binds the function to the call.

Gorpik
+1  A: 

This behavior is not undefined, it's explicitly defined: virtual functions are not virtual in constructors and destructors. They call the static version of the function. If the function is pure virtual, this results in the famous "pure virtual call" error in VC.

I've seen an amusing variation of this in a multithreaded program: an object is being destructed on thread A, while thread B is attempting to call a virtual function. There was no virtual function call in the constructor or destructor, but we still got hit with a pure virtual call error.

Terry Mahaffey