Virtual Functions (C++)

From RAD Studio
Jump to: navigation, search

Go Up to Virtual Functions Index

virtual functions allow derived classes to provide different versions of a base class function. You can use the virtual keyword to declare a virtual function in a base class. By declaring the function prototype in the usual way and then prefixing the declaration with the virtual keyword. To declare a pure function (which automatically declares an abstract class), prefix the prototype with the virtual keyword, and set the function equal to zero.

virtual int funct1(void);       // A virtual function declaration.
virtual int funct2(void) = 0;   // A pure function declaration.

A function declaration cannot provide both a pure-specifier and a definition.

Example:

struct C {
    virtual void f() { } = 0; // ill-formed
};

The only legal syntax to provide a body is:

struct TheClass
{
  virtual void funct3(void) = 0;
};
virtual void TheClass::funct3(void)
{
   // Some code here.
};

Note: See Abstract classesAbstractClasses for a discussion of pure virtual functions.

When you declare virtual functions, keep these guidelines in mind:

  • They can be member functions only.
  • They can be declared a friend of another class.
  • They cannot be a static member.

A virtual function does not need to be redefined in a derived class. You can supply one definition in the base class so that all calls will access the base function.

To redefine a virtual function in any derived class, the number and type of arguments must be the same in the base class declaration and in the derived class declaration. (The case for redefined virtual functions differing only in return type is discussed below.) A redefined function is said to override the base class function.

You can also declare the functions int Base::Fun(int) and int Derived::Fun(int) even when they are not virtual. In such a case, int Derived::Fun(int) is said to hide any other versions of Fun(int) that exist in any base classes. In addition, if class Derived defines other versions of Fun(), (that is, versions of Fun() with different signatures) such versions are said to be overloaded versions of Fun().

Virtual function return types

Generally, when redefining a virtual function, you cannot change just the function return type. To redefine a virtual function, the new definition (in some derived class) must exactly match the return type and formal parameters of the initial declaration. If two functions with the same name have different formal parameters, C++ considers them different, and the virtual function mechanism is ignored.

However, for certain virtual functions in a base class, their overriding version in a derived class can have a return type that is different from the overridden function. This is possible only when both of the following conditions are met:

  • The overridden virtual function returns a pointer or reference to the base class.
  • The overriding function returns a pointer or reference to the derived class.

If a base class B and class D (derived publicly from B) each contain a virtual function vf, then if vf is called for an object d of D, the call made is D::vf(), even when the access is via a pointer or reference to B. For example,

struct X {};// Base class.
struct Y : X {};// Derived class.
struct B {
   virtual void vf1();
   virtual void vf2();
   virtual void vf3();
   void f();
   virtual X* pf();// Return type is a pointer to base. This can
  //  be overridden.
};
class D : public B {
public:
   virtual void vf1();// Virtual specifier is legal but redundant.
   void vf2(int);// Not virtual, since it's using a different
 //  arg list. This hides B::vf2().
// char vf3();// Illegal: return-type-only change!
   void f();
   Y*   pf();// Overriding function differs only
   //  in return type. Returns a pointer to
 //  the derived class.
};
void extf()
{
   D d;// Instantiate D
   B* bp = &d;// Standard conversion from D* to B*
  // Initialize bp with the table of functions
// provided for object d. If there is no entry for a
  // function in the d-table, use the function
     //  in the B-table.
   bp->vf1();  // Calls D::vf1
   bp->vf2();  // Calls B::vf2 since D's vf2 has different args
   bp->f();    // Calls B::f (not virtual)
   X* xptr = bp->pf();// Calls D::pf() and converts the result
   //  to a pointer to X.
   D* dptr = &d;
   Y* yptr = dptr->pf();// Calls D::pf() and initializes yptr.
   //  No further conversion is done.
}

The overriding function vf1 in D is automatically virtual. The virtual specifier can be used with an overriding function declaration in the derived class. If other classes will be derived from D, the virtual keyword is required. If no further classes will be derived from D, the use of virtual is redundant.

The interpretation of a virtual function call depends on the type of the object it is called for; with nonvirtual function calls, the interpretation depends only on the type of the pointer or reference denoting the object it is called for.

virtual functions exact a price for their versatility: each object in the derived class needs to carry a pointer to a table of functions in order to select the correct one at run time (late binding).

See Also