The specific use of C++ Virtual keyword

  • 2020-11-20 06:11:15
  • OfStack

Basic understanding and demo

Ordinary inheritance


#include<iostream>

class Parent {
public:
  void print() {
    std::cout << "Parent" << std::endl;
  }
};

class Child : Parent {
public:
  void print() {
    std::cout << "Child" << std::endl;
  }
};

int main() {
  Child c;
  c.print();
  return 0;
}

The output result is "Child"

But in the case of "superclass pointer to subclass object", what happens if you use the superclass pointer to call the overridden method? By its syntactic nature, the first half of a subclass object's memory is a superclass, because a pointer to a subclass object can be converted directly to a superclass.


#include<iostream>

class Parent {
public:
  void print() {
    std::cout << "Parent" << std::endl;
  }
};

//  Notice that it has to be  public Parent
//  Otherwise, you'll get an error  cannot cast 'Child' to its private base class 'Parent'
class Child : public Parent {
public:
  void print() {
    std::cout << "Child" << std::endl;
  }
};

int main() {
  Parent* p = new Child();
  p->print();
  return 0;
}

The output in this case is "Parent"  .

Therefore, when a member function needs to be overridden by a subclass, it must be declared as virtual, which is a virtual function. Note that the virtual keyword of the method overridden by a subclass is automatically inherited and can be written explicitly or unwritten (it is recommended to write).

This will be fine after the modification:


#include<iostream>

class Parent {
public:
  virtual void print() {
    std::cout << "Parent" << std::endl;
  }
};

class Child : public Parent {
public:
  virtual void print() {
    std::cout << "Child" << std::endl;
  }
};

int main() {
  Parent* p = new Child();
  p->print();
  return 0;
}

Deepen the understanding

An important concept of the Virtual keyword is "polymorphism is only useful if it is indirectly pointed to a derived class subtype by base class pointer or reference", that is, the function call of the base class is called according to the polymorphism if there is virtual, and if there is no virtual, it is a normal static function call, or the call to the base class.

For example


#include <iostream.h>
class Base
{
public:
virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
void g(float x){ cout << "Base::g(float) " << x << endl; }
void h(float x){ cout << "Base::h(float) " << x << endl; }
};
 
class Derived : public Base
{
public:
virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
void g(int x){ cout << "Derived::g(int) " << x << endl; }
void h(float x){ cout << "Derived::h(float) " << x << endl; }
};
 
void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3 (surprise!)
// Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}

This is what I posted on this blog in a paragraph:

bp and dp point to the same 1 address, and the results of the operation are supposed to be the same, but in fact the results of the operation are different, so he attributed the reason to the hidden rules of C++, but this view is wrong. It is not the address to which bp and dp call functions run, but the type of pointer to which they point. "Polymorphism only comes into play when indirectly pointing to derived class subtypes through base class Pointers or references" (C++ Primer 3rd Edition). pb is the base class pointer, pd is derived class pointer, pd all function calls are just call its own function, and polymorphism is independent, so pd all function calls output Derived:: is completely normal; If there is virtual, the function call of pb will call the derived class according to the polymorphism. If there is no virtual, it will be normal static function call, or call the base class. Therefore, the function call of f with virtual will output Derived::, and the other two without virtual will output Base::. So there's no such thing as a hidden rule, and while the High Quality C++/C Programming Guide is a great book, don't get superstitious. Remember that "polymorphism is only useful if it is referred indirectly to a derived class subtype by a base class pointer or reference".


Related articles: