1.用类定义对象的时候,C++编译器为每个对象编了一个一维的虚函数表(简称虚表)。这个虚函数表存储了类中虚函数的函数入口地址;
2.用类定义对象的时候,C++编译器还为每个对象分配了一个指向虚表开始地址的指针vptr;
3.当代码运行时,C++编译器首先判断父类指针调用的函数是不是虚函数。
如果不是虚函数,则是普通函数。如果函数实参传的是父类对象的地址(或父类对象的引用),则父类指针调用父类对象中的普通函数;如果实参传的是子类对象的地址(或子类对象的引用),则父类指针调用子类中继承自父类的普通函数。
如果是虚函数,则利用vpyr指针找到虚表,根据虚表找到虚函数的入口地址,直接执行函数代码。这个过程C++编译器不需判断传来的实参是父类对象的地址(或引用)还是子类对象的地址(或引用)
以如下的代码为例:
1 #include<iostream> 2 using namespace std; 3 4 class Parent 5 { 6 public: 7 Parent(int _a):a(_a){} 8 ~Parent(){cout<<"执行Parent类析构函数"<<endl;} 9 virtual void print(){cout<<"打印Parent类 a:"<<a<<endl;} 10 11 public: 12 int a; 13 }; 14 15 16 class Child:public Parent 17 { 18 public: 19 Child(int _a,int _a_,int _b):Parent(_a),a(_a_),b(_b){} 20 ~Child(){cout<<"执行Child类析构函数"<<endl;} 21 void print(){cout<<"打印Child类 b:"<<b<<endl;} 22 23 public: 24 int a; 25 int b; 26 }; 27 28 29 void fun(Parent* p) 30 { 31 p->print(); 32 } 33 34 35 36 int main() 37 { 38 Parent p(5); 39 Child c(20,30,40); 40 41 fun(&p); 42 fun(&c); 43 44 return 0; 45 }
代码的执行结果为:
打印Parent类 a:5
打印Child类 b:40
执行Child类析构函数
执行Parent类析构函数
执行Parent类析构函数
分析:代码中的fun()函数调用的print()函数是虚函数,则走的是vptr-->虚表-->虚函数代码这条路线