Consider the following sample code:
#include <iostream>
using namespace std;
class base
{
public:
base()
{
bar(); //Line1
this->bar(); //Line2
base *bptr = this;
bptr->bar(); //Line3
((base*)(this))->bar(); //Line4
}
virtual void bar() = 0;
};
class derived: base
{
public:
void bar()
{
cout << "vfunc in derived class\n";
}
};
int main()
{
derived d;
}
The above code has pure virtual function bar()
in base class which is overriden in the derived class. The pure virtual function bar()
has no definition in base class.
Now focus on Line1
, Line2
, Line3
and Line4
.
I understand : Line1
gives compilation error, because pure virtual function cannot be called from ctor.
Questions:
Why does Line2
and Line4
give no compilation error
for the same reason mentioned in I understand
statement above?. The calls in Line2
and Line4
will eventually cause linker-error
only.
Why does Line3
give neither compilation error nor linker error but gives run-time exception
only ?
Real-Life example of UB when Pure virtual function call through constructor:
A pure virtual function (or abstract function) in C++ is a virtual function for which we can have implementation, But we must override that function in the derived class, otherwise the derived class will also become abstract class (For more info about where we provide implementation for such functions refer to this https://stackoverflow.
In short, objects are constructed from the base up to the derived. So when you try to call a virtual function from the base class constructor, overriding from derived classes hasn't yet happened because the derived constructors haven't been called yet. Share Improve this answer
Answer: You get the dreaded purecall error, because the base class constructor has engaged in a conspiracy with the function call_f to call the function f from its constructor. Since f is a pure virtual function, you get the purecall error. Okay, next question: Why didn’t the original code result in a compiler error?
In our discussion __purecall , we saw that you can declare a pure virtual function with the = 0 syntax, and if you try to call one of these functions from the base class, you will get the dreaded R6025 – pure virtual function call error.
In all four cases, the behaviour is undefined; so exactly what happens depends on what your compiler happens to do in the face of invalid input.
The compiler might attempt to diagnose the problem to give a warning; this is easy to do for Line 1, and more difficult for the other lines, which would explain why you only see a warning for Line 1.
When calling a virtual function from a constructor, the compiler knows which overload should be called, and so it might generate a static call. This is why you get a link error from Line 2 and Line 4.
In Line 3, the compiler must have decided that it's too difficult to work out whether it can generate a static call, so it generated a dynamic call instead. Tracking the value of a variable is rather harder than working out that a temporary pointer must refer to this
, and often not possible at all. That's why you get a run-time error there.
Of course, all of this is undefined behaviour, and might change from compiler to compiler, or according to the phase of the moon.
If the function had an implementation, then it would be valid to call it statically, as Base::bar()
, or bptr->Base::bar()
. Calling it dynamically would still give undefined behaviour.
Calling an Pure virtual function from constructor is an Undefined Behavior & the compiler is free to show any behavior.
Reference:
C++03 Standard 10.4/6:
"Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined."
The C++ standard defines Undefined behavior in:
[defns.undefined] 1.3.12 undefined behavior
behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements. Undefined behavior may also be expected when this International Standard omits the description of any explicit definition of behavior. [Note: permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. ]
I can partially answer. Line 3 requires that the compiler do data flow analysis to determine that the function Is not being called on another fully constructed object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With