一.抽象类
纯虚函数:在虚函数后面加上=0就是纯虚函数
作用:纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承
抽象类定义:
包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象
举例:
class Person
{
public:
virtual void func() = 0
{
cout << "Person func()" << endl;
}
};
class Student : public Person
{
public:
//必须重写虚函数
virtual void func()
{
cout << "Student func()" << endl;
}
};
上面我们提出了接口继承,下面我们就来带大家认识什么是接口继承/实现继承???
实现继承:
是指对普通函数来说,派生类继承了基类的函数,派生类可以使用该函数,继承的是函数实现过程
接口继承:
是指对虚函数来说,继承的是函数接口,派生类可以对函数实现进行重写,从而达到多态的目的,这里也说明,如果不是为了实现多态,不建议写成虚函数形式
二.多态的原理
如果你看过我的上一篇博客的话,相信对虚函数表(简称虚表)不会陌生,如果你对此一点都不了解,建议回去看看我的上一篇多态的博客
下面请看两个问题:
虚函数存在哪的?虚表存在哪的?
虚函数显然是和普通函数一样,都是存在代码段的,那么虚表呢?也是在代码段吗?关于这个问题,我们可以去验证下:
class Person
{
public:
virtual void func()
{
cout << "Person" << endl;
}
};
class Student : public Person
{
public:
void func()
{
cout << "Student" << endl;
}
};
int main()
{
int i = 0;
static int j = 1;
int* p1 = new int;
const char* p2 = "xxxxxxxx";
printf("栈:%p\n", &i);
printf("静态区:%p\n", &j);
printf("堆:%p\n", p1);
printf("常量区:%p\n", p2);
Person* p3 = new Person;
Person* p4 = new Student;
printf("Base虚表地址:%p\n", *(int*)p3);//注意:类型转换,再解引用找地址
printf("Base虚表地址:%p\n", *(int*)p4);
return 0;
}
结果:
通过对比发现,虚表距离常量区最近,所以我们可以认为虚表是存在代码区
所以我们现在来综合看待普通函数调用和多态调用?
普通函数调用实际上是在编译时确定的,即在编译时确定函数地址,然后在链接是通过符号表来找到函数位置
多态调用实际上是在运行时到对象的中取找的,函数地址存放在虚表中,但函数是存放在代码区的
三.动态绑定与静态绑定
定义:
1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,
比如:函数重载
2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体
行为,调用具体的函数,也称为动态多态。
四.单继承和多继承关系的虚函数表
关于单继承的虚表我们之前一直在讲,现在我们就直接上多继承的虚表
请看下面代码:
class Base1
{
public:
virtual void func1()
{
cout << "Base1::func1" << endl;
}
virtual void func2()
{
cout << "Base1::func2" << endl;
}
private:
int b1;
};
class Base2
{
public:
virtual void func1()
{
cout << "Base2::func1" << endl;
}
virtual void func2()
{
cout << "Base2::func2" << endl;
}
private:
int b2;
};
class Derive : public Base1, public Base2
{
public:
virtual void func1()
{
cout << "Derive::func1" << endl;
}
virtual void func3()
{
cout << "Derive::func3" << endl;
}
private:
int d1;
};
我们利用Derive继承Base1和Base2,现在问题是我们在Base1和Base2都有func1和func2,那么我们的派生类Derive的虚表是什么情况呢???
如果我们还是按照之前情况,将派生类的生成一个虚表,那么如果我们对两个基类就会出现二义性,所以实际上是在派生类建立两个虚表,分别将基类1和基类二虚函数放入两个不同的虚表中,那么如果是派生类的独立虚函数呢?此时就会放入第一个继承的虚表中
实际如下图:
最后,感谢大家的支持!!!