The usage of virtual function and pure virtual function in C++

  • 2020-04-02 02:37:32
  • OfStack

This paper deeply analyzes the usage of virtual function and pure virtual function in C++, which is very important for learning and mastering object-oriented programming. The specific contents are as follows:

First of all, The core ideas of object-oriented programming are data abstraction, inheritance and dynamic binding . With data abstraction, you can separate the interface of a class from its implementation, use inheritance, you can more easily define new classes that are similar but not identical to other classes, and with dynamic binding, you can ignore the differences between similar classes to some extent and use their objects in a unified way.

The purpose of a virtual function is to achieve Polymorphism, which separates the interface from the implementation in a common approach but with different strategies depending on individual differences. A pure virtual function is a special virtual function. Virtual functions are associated with polymorphism, and polymorphism with inheritance. So this article is all about inheritance. Without inheritance, there's nothing to talk about.

I. virtual functions

Definition 1.

In C++, a base class must distinguish between its two member functions: those that the base class wants its derived classes to override; The other is a function that the base class wants the derived class to inherit directly without changing. For the former, the base class defines the function as virtual by prepending it with the virtual keyword.


class Base{ //The base class
public: 
  virtual int func(int n) const; 
}; 
 
class Derive_Class : public Base{ 
public: 
  int func(int n) const; //The default is also virtual
}; 

When we override a function in a derived class, we can prefix the function with a virtual keyword. This is not necessary, however, because once a function is declared virtual, it is virtual in all derived classes. Any non-static function other than a constructor can be a virtual function. A derived class often (but not always) overrides the virtual functions it inherits, and if a derived class does not override a virtual function in its base class, the virtual function behaves like any other ordinary member, and the derived class directly inherits its version in the base class.

2. Dynamic binding

Dynamic binding occurs when we call a virtual function using a reference (or pointer) to the base class. Because we don't know which version of the virtual function is being called until runtime, either in a base class or a derived class, depending on the true type of the object to which the reference (or pointer) is bound. Unlike compile-time binding for non-virtual functions, virtual functions select the version of the function at run time, so dynamic binding is also called run-time binding.

3. Static and dynamic typing

Static typing refers to the type of a variable when it is declared or the type generated by an expression, which is always known at compile time. A dynamic type is the type of an in-memory object represented by a variable or expression that is not known until run time. The call is resolved at run time if and only if a virtual function is called through a pointer or reference to the base class, and only if the dynamic type of the object is likely to differ from the static type. If the expression is neither a reference nor a pointer, its dynamic type is always the same as its static type.

4. Final and override

If a function is defined in a derived class with the same name as a virtual function in the base class but with a different list of parameters, the compiler considers it a newly defined function in the derived class. If our intention was to override a virtual function, this error is hard to detect. Make the intent clearer by adding the override keyword at the end of a virtual function in a derived class. If we tag a function with override, but the function does not override the existing virtual function, the compiler will report an error.


class Base{ //The base class
public: 
  virtual int func(int a, int b) const; 
}; 
 
class Derive_Class : public Base{ 
public: 
  int func(int a) const override; //Error reported, no virtual function overridden
}; 

If we define a class, we don't want it to be inherited. Or if you want a function not to be overridden, you can specify the class or function as final, and any subsequent attempts to inherit the class or overwrite the function will cause an error.


class Base final {  };   //Base classes cannot be inherited
class Derive_Class : public Base {  };   //An error
 
void func(int) const final;  //Subsequent classes are not allowed to override func(int)

5. The mechanism of avoiding virtual functions

In some cases, we want calls to virtual functions not to be dynamically bound, but to be forced to execute a particular version of the virtual function. You can do this using the scope operator.


//Forcibly calling the version of the function defined in the base class regardless of the dynamic type of baseP
int a = baseP->Base::func(42); 

If a derived class virtual function needs to call its base class version, but does not use the scope operator, the call is resolved at run time as a call to the derived class version itself, resulting in infinite recursion.

Pure virtual functions

Definition 1.

To facilitate the use of polymorphism, we often need to define virtual functions in the base class. In many cases, virtual functions cannot be meaningfully implemented in a base class. In order to make virtual functions do nothing in the base class, the concept of "pure virtual functions" is introduced so that functions do not need to be defined. We can declare a virtual function as pure virtual by writing =0 at the position of the function body (that is, before the semicolon of the declaration statement). Where, =0 can only appear in the virtual function declaration statement inside the class:


class Base{ //Abstract base class
public: 
  virtual int func(int n) const =0; 
}; 

Note that we can also provide definitions for pure virtual functions, but the body of the function must be defined outside of the class.

2. Abstract base classes

Classes that contain (or directly inherit without overwriting) pure virtual functions are called abstract base classes. The abstract base class is responsible for defining the interface, which can be overridden by subsequent classes. If the derived class does not redefine pure virtual functions, but simply inherits pure virtual functions from the base class, then the derived class is still an abstract base class. Because the abstract base class contains pure virtual functions (not defined), we cannot create an object for the abstract base class, but we can declare a pointer or reference to the abstract base class.


Base base;  //Error, unable to instantiate abstract base class

Conclusion:

Virtual functions must be implemented, do not implement the compiler will report an error.

Both parent and child classes have their own versions of virtual functions. Dynamically bound at runtime by polymorphism.

The scope operator can be used to force the specified virtual function version.

(4). Pure virtual function declaration as follows: virtual void funtion()=0; Pure virtual functions don't have to be defined. Classes that contain pure virtual functions are abstract base classes, which cannot create objects but can declare Pointers or references to abstract base classes.

. After the derived class implements the pure virtual function, the pure virtual function becomes a virtual function in the derived class, and its subclasses can override the function.

6. Destructors should generally be virtual functions, which ensures that the correct destructor version is called at destructor time.


Related articles: