可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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:
回答1:
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. ]
回答2:
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.
回答3:
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.
回答4:
Which compiler are you using?
Vc10 and gcc 4.6 all compile fine. gcc give a nice warning about calling virtual function from constructor will be just using the base::bar() function instead of polymorphic.
This is probably bocs calling virtual from constructor is Undefined Behavior.
回答5:
Weird C++ fact: it is legal (and, very rarely, useful) to define a pure virtual function.
You could try adding
void base::bar() { cout << "Wuh?"; }
and you will find line 2 and line 4 invoke that function.
The legal way to call that definition is to do it explicitly: base::bar();
The compiler has managed to optimise the virtual call (which will fail) to the non-virtual call in those cases. It is neither required nor prevented from doing that; all your calls have undefined behaviour.
回答6:
Your code contains undefined behavior, so whatever the compiler does is
correct. All of the calls to bar()
in your code require dynamic
resolution, and would result in a call to a pure virtual function; this
is undefined behavior. (You can call Base::bar()
if you write it like
that and the function exists, since no dynamic resolution is required.)
The fact that the code compiles doesn't mean that it will run
successfully; in the case of g++, for example, I'm fairly sure that it
will crash with an error message.
Whether the compiler complains or not probably depends on how much
effort it goes to to resolve the dynamic resolution at compile time.
Without optimization, it almost certainly can't resolve 3 at compile
time, but I'm somewhat surprised that it treats 1 and 2 differently.
And the statement that "pure virtual function cannot be called from
constructor" is false. The only time there is a problem is when dynamic
resolution resolves to a pure virtual function. Calling it with static
resolution (assuming it exists) is fine, and calling a pure virtual
function in a base class is fine if the dynamic resolution turns up a
non pure virtual function in a derived class whose constructor has
started or has run.