当一个类中有一个或多个虚函数时,内存中会多一个虚指针(vptr,virtual pointer),指向一个虚表(vtbl,virtual table)
父类有虚函数,则子类一定有虚函数
在下图示意图中,我们可知,B继承A,C继承B。
A有两个虚函数,B继承A则也有两个虚函数,同时B对虚函数vfunc1进行了重写,同理,C继承B,则C也有两个虚函数,C对虚函数vfunc1也进行了重写。故在内存中,A B C对应的虚函数vfunc2是同一个均为A::vfunc2,对应的虚函数vfunc1分别为A::vfunc1,B::vfunc1,C::vfunc1。
c++编译器遇到函数调用时,会有两个考量,是静态绑定还是动态绑定,静态绑定是call xxx地址,即一定调用到某个地址,动态绑定可以调用到不同的地址
动态绑定需符合三个条件:
- 通过指针调用(对象调用函数是静态绑定)
- 指针是向上的关系,向上关系是指,比如 父类 名称 = new 子类(),这样的关系即是一种向上的关系
- 调用的是虚函数
虚函数的这种用法,称之为多态。
示例程序:
#include<iostream>
using namespace std;class A {
public:A(int data1, int data2) :m_data1(data1), m_data2(data2) {}virtual void vfunc1() { cout << "调用的是A的vfunc1"<< endl; }virtual void vfunc2() { cout << "调用的是A的vfunc2" << endl; }void func1() { cout << "调用的是A的func1" << endl; }void func2() { cout << "调用的是A的func2" << endl; }void getData() {cout << "A的m_data1:" << m_data1 << ",A的m_data2:" << m_data2 << endl;}
private:int m_data1, m_data2;
};class B:public A {
public:B(int n1,int n2,int n3) :A(n1,n2), m_data3(n3){}virtual void vfunc1() { cout << "调用的是B的vfunc1" << endl; }void func2() { cout << "调用的是B的func2" << endl; }void getData() {A::getData();cout << "B的m_data3:" << m_data3 << endl;}
private:int m_data3;
};class C :public B {
public:C(int n1, int n2, int n3,int n4,int n5):B(n1,n2,n3) , m_data1(n4), m_data4(n5) {}virtual void vfunc1() { cout << "调用的是C的vfunc1" << endl; }void func2() { cout << "调用的是C的func2" << endl; }void getData() {B::getData();cout << "C的m_data1:" << m_data1 << ",C的m_data4:" << m_data4 << endl;}
private:int m_data1, m_data4;
};
测试程序:
int main()
{A a(1,2);a.vfunc1();a.vfunc2();a.func1();a.func2();a.getData();cout << endl;B b(3,4,5);b.vfunc1();b.vfunc2();b.func1();b.func2();b.getData();cout << endl;C c(6,7,8,9,10);c.vfunc1();c.vfunc2();c.func1();c.func2();c.getData();cout << endl;A* d = new B(1,3,5);d->vfunc1();d->vfunc2();d->func1();d->func2();d->getData();cout << endl;A* d2 = new C(1, 3, 5,7,9);d2->vfunc1();d2->vfunc2();d2->func1();d2->func2();d2->getData();system("pause");return 0;
}
输出结果:
调用的是A的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是A的func2
A的m_data1:1,A的m_data2:2调用的是B的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是B的func2
A的m_data1:3,A的m_data2:4
B的m_data3:5调用的是C的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是C的func2
A的m_data1:6,A的m_data2:7
B的m_data3:8
C的m_data1:9,C的m_data4:10调用的是B的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是A的func2
A的m_data1:1,A的m_data2:3调用的是C的vfunc1
调用的是A的vfunc2
调用的是A的func1
调用的是A的func2
A的m_data1:1,A的m_data2:3
从输出结果中,我们可以看出,当对函数进行重写后,子类在调用函数时会调用重写后的函数。当函数是虚函数时,若我们按照父类 名称 = new 子类() 的形式生成指针,则该指针在调用函数时,若调用的函数是虚函数,则其会调用子类重写后的虚函数,若调用的函数不是虚函数,则无论子类对该函数是否重写,调用的均为父类的函数。