在什么情况下会使用多态,在定义一套接口时,如这个接口void compare(Student &st, StudyBySelf &sb),定义的是父类指针
但是在传入子类指针的时候希望执行的是子类的函数如 compare(pr, sb);,但是if (st.educational() > sb.educational())传入的是pr希望调用Primal这个子类的educational()函数,此时就需要有虚函数了,如果没有virtual关键字那无论你传入的是子类还是父类对象,都只会调用父类的educational()函数。
//多态使用的三个条件
//1、要有继承2、要有虚函数重写3、用父类指针(父类引用)指向子类对象
#include<iostream>
using namespace std;
class Student
{
public:
int knowledge;
public:
virtual int educational()
{
cout << "st knowledge=" << knowledge << endl;
return knowledge;
}
};
class Primal :public Student
{
public:
virtual int educational()
{
cout << "pr knowledge=" << knowledge << endl;
return knowledge;
}
};
class Middle :public Student//1、要有继承
{
public:
virtual int educational()//2、要有虚函数重写 //有virtual关键字 会放入虚函数表中
{
cout << "mi knowledge=" << knowledge << endl;
return knowledge;
}
};
class StudyBySelf
{
public:
int knowledge;
public:
int educational()
{
cout << "sb knowledge=" << knowledge << endl;
return knowledge;
}
};
//多态定义一套接口可以子类父类动态使用
//给对象搭建舞台
void compare(Student &st, StudyBySelf &sb)
{
if (st.educational() > sb.educational())
//这里实现的效果:传来子类的对象则执行子类的educational函数
//传来父类对象则执行父类的educational函数
//c++编译器根本不区分是子类对象还是父类对象
//父类对象和子类对象分别都有vptr指针===>指针指向虚函数表===>指向函数入口地址
//运行时c++编译器才会去判断是不是虚函数在不在虚函数表中
{
cout << "school is good" << endl;
}
else
{
cout << "self is good" << endl;
}
}
void main2()
{
Student st;//在定义类对象的时候c++编译器会在对象中添加一个vptr指针
st.knowledge = 30;
Primal pr;//子类也会有vptr指针
pr.knowledge = 20;
Middle mi;
mi.knowledge = 40;
StudyBySelf sb;
sb.knowledge = 25;
compare( st, sb);//3、用父类指针(父类引用)指向子类对象
compare(pr, sb);
compare(mi, sb);
system("pause");
}
虚函数表:
1、当类中声明虚函数时,编译器会在类中生成一个虚函数表
2、虚函数表是一个存储类成员函数指针的数据结构
3、虚函数表是由编译器自动生成与维护的
4、virtual成员函数会被编译器放入虚函数表中
5、存在虚函数时,每个对象都有一个指向虚函数表的指针(vptr指针)
编译器确定函数是否为虚函数
1、若函数不是虚函数,编译器课直接确定被调用的成员函数(静态链编,根据父类的类型来确定)
2、函数是虚函数,编译器根据对象的vptr指针所指的虚函数表中查找对应函数,并调用。(查找和调用在运行阶段,是动态链编)
虚函数指针的初始化:1、初始化Primal pr的vptr指针,初始化时分步的
2、当需要执行父类的构造函数时,pr.vptr指向父类的虚函数表,当父类的构造函数
执行完毕后,会把pr.vptr指向子类的虚函数表
3、结论:子类的pr.vptr指针是分步完成的。
不要用父类指针做赋值指针变量,去遍历一个子类数组:
父类的虚函数指针步长和子类虚函数指针的步长的区别,指针步长就是指针的类型,父类虚函数指针的类型是父类类型,子类虚函数指针是子类类型,类类型的大小是指成员变量的大小,在子类继承父类的时候没有添加自己的属性(也就是没有添加自己的成员变量)此时子类虚函数指针的步长和父类虚函数指针的步长一样,但是当子类添加了自己的属性的时候,子类和父类的步长就不一样了。此时在父类指针指向子类对象数组的时候,vptr++就会出错。因为步长不一样。