How to implement pure virtual function in C++
I think the implementation of virtual function is talked about a lot. My question is what about pure virtual function? However it is implemented? In virtual table, how to tell it is a pure or non-pure? What the difference between pure virtual function and virtual 开发者_开发问答function with implementation?
There is no usually no implementation difference between pure and non-pure virtual functions. Provided a pure-virtual function is defined, it acts like any other virtual function. If it is not defined, it only causes a problem if it is explicitly called.
There are two major differences in behaviour, but there is usually no impact on the implementation of the virtual function mechanism itself. The compiler must not allow you to construct an object of a type which has pure virtual functions that don't have a non-pure final override in their inheritance hierarchy and any attempt to make a virtual call to a pure virtual function directly or indirectly from an object's constructor or destructor causes undefined behaviour.
virtual void foo() = 0;
A pure virtual function is still a virtual function, so it would be in the vtable, but the compiler doesn't require an implementation for it, and will prohibit instantiation of the base class that declares the pure virtual. Since you can still dereference pointers of type of the abstract base class, the virtual table has to have an entry, for polymorphism and runtime binding.
Does that help?
This is not an answer, but rather a follow up to the comments to this answer above
The C++ language defines how the virtual dispatch mechanism takes place during construction. When instantiating an object in a hierarchy, the base constructor gets called (*). At that point, the virtual dispatch mechanism is initialized for the base class. At this stage, virtual functions will be dispatched to the base implementation. Any call to a non-pure virtual method using the virtual dispatch mechanism (without explicit class qualification) will call the base implementation.
After the base constructor is completed, the virtual dispatch mechanism (usually vtable) gets reseted to the derived type version, and any dynamic call from there on will call the derived version of the methods:
struct base {
virtual void non_pure() { std::cout << "base::non_pure" << std::endl; }
virtual void pure_not_implemented() = 0;
virtual void pure_implemented() = 0;
base() { // at this point the object is a ´base´
non_pure(); // base::non_pure
// pure_not_implemented(); // runtime error: pure virtual method called
pure_implemented(); // base::pure_implemented
// base::pure_not_implemented(); // link error
}
};
void base::pure_implemented() { std::cout << "base::pure_implemented" << std::endl; }
struct derived : base {
virtual void non_pure() { std::cout << "derived::non_pure" << std::endl; }
virtual void pure_not_implemented() { std::cout << "derived::pure_not_implemented" << std::endl; }
virtual void pure_implemented() { std::cout << "derived::pure_implemented" << std::endl;
derived() { // after the implicit call to the base class
// this is a ´derived´ object, now calls will
// get dispatched to derived:: implementations
non_pure(); // derived::non_pure
pure_not_implemented(); // derived::pure_not_implemented
pure_implemented(); // derived::pure_implemented
base::non_pure(); // base::non_pure
// base::pure_not_implemented() // link error
}
};
Note that there are differences between using the dynamic dispatch mechanism (usually vtable) and calling an specific method with the fully qualified name. A call to a non-implemented pure virtual method through the full qualification will be allowed at compile time but will fail at link time (the linker is not able to locate an implementation to call).
This is an important decision in the design of the language. The other option (the one Java took) is initializing the virtual dispatch mechanism to the most derived type from the beginning, previous to calling the base class constructor. The problem this approach has is that if the base constructor calls a virtual method (which in Java all are) it will get dispatched to the most derived implementation and will thus execute in a not-yet constructed object, possibly causing unexpected results:
public class Base
{
public Base() {
f();
}
public void f() {
System.out.println("Base.f");
}
}
public class Derived extends Base {
public final int constant;
public Derived() { constant = 5; }
public void f() {
System.out.println( "Derived.f() " + constant );
}
public static void main( String args[] ) {
Derived d = new Derived(); // prints Derived.f() 0
}
}
In the Java version, the dynamic dispatch mechanism considers the object to be of type Derived
from the beginning. The call to f()
in the base constructor will be dynamically dispatched to the derived implementation. In the example above, even if the variable is declared as final and thus a constant by the value of 5 (seems obvious in the code), the actual printed value is 0 because the constructor of Derived
has not executed.
(*) This is oversimplifying, but the details don't really affect the argument.
I do not know the actual implementation, but a good choice would be to implement it as a NULL
pointer in the vtable
. In other words, if you have an implementation there is a valid function pointer in vtable
and if it is pure virtual, you have a NULL
pointer.
This is so logical that I even think it is implemented this way:-).
精彩评论