5

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?

skydoor
  • 25,218
  • 52
  • 147
  • 201
  • 1
    http://stackoverflow.com/questions/2156634/why-pure-virtual-function-is-initialized-by-0 –  Mar 11 '10 at 22:29

4 Answers4

4

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.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • It's undefined behavior if it's called virtually. If the function has an implementation, it can be called in the form `AbstractBase::Foo()`. – David Thornley Mar 11 '10 at 21:19
  • If it is not defined, you tend to get a linker error, if you try to call it explicitly? - But how can you call it virtually, if you cannot have an instance of an abstract type? – UncleBens Mar 11 '10 at 21:23
  • @UncleBens: I don't know, I was just paraphrasing part of 10.4 of the Standard. – David Thornley Mar 11 '10 at 21:26
  • 2
    @UncleBens: You cannot instantiate an abstract class (a class with at least a virtual function) but you can instantiate a derived class that is not abstract. When the derived class is being instantiated the base subobject is first constructed. At that particular instant in time (during the execution of the base constructor) the object is of type base, and the virtual dispatch mechanism is that of the base class. Trying to call a non-pure virtual method will call the base implementation (the object is still base), and calling a pure virtual method will in most cases kill your application. – David Rodríguez - dribeas Mar 11 '10 at 21:44
  • @dribeas: I tried, and just got a linker error... Just to be sure: calling a non-pure virtual function from constructor is defined (but doesn't call the method you might expect), whereas calling a pure virtual function - even if it happens to be defined - from constructor is undefined behavior? (If so, it's very bizarre - why couldn't it just be a compiler error?) – UncleBens Mar 11 '10 at 21:49
  • The way that objects are initialized is a design decision in the language and other languages have followed different paths. In Java, the type of the object being initialized is the most derived type at all times, and calling a virtual method in the constructor will call the most derived implementation. The main design difference there is that in doing so, Java allows you to call a method in a yet-unconstructed object (while constructing the base object, the derived object has not yet been initialized, but the virtual method call will execute at the derived level). – David Rodríguez - dribeas Mar 11 '10 at 21:51
  • @UncleBens: I have tried to explain in the format of an answer below with some code examples (http://stackoverflow.com/questions/2428684/how-to-implement-pure-virtual-function-in-c/2429122#2429122) – David Rodríguez - dribeas Mar 11 '10 at 22:37
  • @UncleBens: in n3035 (draft standard) I found this: > 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. – Jan Mar 12 '10 at 10:47
3

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?

codenheim
  • 20,467
  • 1
  • 59
  • 80
  • 1
    Declaring a pure virtual function causes a entry to be added for the function in the vtable. In the class that declares the pure virtual function (and in derived classes that don't override it), most implementations set this entry to invoke some sort of diagnostic function that displays some soft of error message (e.g. "Error: pure virtual function called"). It is still possible to define a pure virtual function, but it must be invoked non-virtually. – Stephen C. Steel Mar 11 '10 at 21:53
  • Oh, you are correct. Thanks. A pure virtual function can also have a definition in the base class. +1 for your correction. – codenheim Mar 12 '10 at 01:57
3

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.

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • I was really wondering why calling a pure virtual from the constructor would be undefined behavior, but calling a non-pure one wouldn't be. Specifically, would it be that hard for compilers to detect? If a call to a pure function is undesired (for whatever reason), why not require compiler diagnostics instead? – UncleBens Mar 12 '10 at 16:13
  • Calling a non-pure virtual method call will have the effect of calling the most derived implementation down-to and including the current type, but non of its descendants. The effect of calling a pure virtual method is undefined as it could depend on the dispatch mechanism. Most compilers will call synthesized code to diagnose and kill the application with a sensible message. – David Rodríguez - dribeas Mar 12 '10 at 16:19
  • At the point where the compiler is processing the constructor it can know if it is an implemented pure virtual method (if it can see the definition), but it cannot guarantee that the pure virtual method is not implemented in a different translation unit (maybe your constructor is inlined in the class declaration and is being included from a translation unit while the pure virtual method is defined in a different translation unit) – David Rodríguez - dribeas Mar 12 '10 at 16:21
0

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:-).

pajton
  • 15,828
  • 8
  • 54
  • 65
  • but pure virtual function can still have implementation, right? – skydoor Mar 11 '10 at 21:13
  • A pure virtual function cannot have an implementation in the class that declares it, otherwise, by definition, it is not a pure virtual function. It has to be implemented in derived classes. – codenheim Mar 11 '10 at 21:15
  • 2
    @skydoor: Yes it can, but the implementation has to be specifically called. It will still prevent the class it's in from having an object declared (and all subclasses that don't override the pure virtual). – David Thornley Mar 11 '10 at 21:16
  • For example, if the only candidate to make a base class abstract is making the destructor pure (not sure why), then you'd still have to implement it, or you won't be able to destroy derived classes. – UncleBens Mar 11 '10 at 21:31
  • @mrjoltcola: You're wrong, you _can_ implement a pure virtual function. (You only cannot implement it in the class definition, it has to be separate.) Since we can't down-vote erroneous comments, I suggest you remove yours. It's confusing. – sbi Mar 11 '10 at 21:35
  • @pajton Actually, Stroustrup in the D&E book says that setting the vtable entry to null is not a good way of implementing a PVF. –  Mar 11 '10 at 22:21
  • @Neil Cool, I didn't know that! It seemed like a good way to do it, but maybe there are perfect reasons not to do so... – pajton Mar 11 '10 at 23:33
  • @mrjoltcola, the "=0" makes it pure, not the "no implementation". It can have an implementation, it just has to be explicitly invoked. Also, technically, derived classes don't *have* to implement a PVF -- if they don't, they just can't be instantiated. – Dan Mar 12 '10 at 02:46
  • @pajton: The reason why setting it to `NULL` isn't all that good is because, when a pure virtual function without implementation gets called, the program crashes without a diagnostic. So compilers usually set it to a stub which outputs a diagnostic ("pure virtual function called") and call `std::abort()`. – sbi Mar 12 '10 at 06:25