多态成立的三个条件
- 有继承
- 有函数重写
- 有父类指针(父类引用)指向子类对象
#include <cstdlib>
#include <iostream>
using namespace std;
//多态成立的三个条件
//要有继承,虚函数重写,父类指针指向子类对象
class Parent
{
private:
int a ;
public:
Parent(int a =0)
{
this->a = a;
}
virtual void print() //1,动手脚 特殊处理 虚函数表
{
cout<<"我是巴巴"<<endl;
}
};
class Child : public Parent
{
private:
int b ;
public:
Child(int a =0,int b =0):Parent(a)
{
this->b = b;
}
virtual void print()
{
cout<<"我是儿子"<<endl;
}
};
void HowToPlay(Parent *base)
{
base->print();//2,动手脚会有多态发生 特殊处理
//传来子类对象,执行子类的print函数,传来父类对象执行父类的print函数
//c++编译器根本不需要区分是子类对象还是父类对象
//父类对象和子类对象分别有vptr指针 ===>虚函数表===>函数的入口地址
//迟邦定(运行时,c++编译器才去判断)
}
int main()
{
Parent p1; //3,动手脚 用类定义对象的时候 c++编译器会在对象 中添加一个vptr指针
Child c1;
HowToPlay(&p1);
HowToPlay(&c1);
system("pause");
return 0;
}
多态原理
- 当类中声明虚函数时,编译器会在类中生成一个虚函数表
- 虚函数表是一个存储类成员函数指针的数据结构
- 虚函数表是由编译器自动生成与维护的
- virtual成员函数会被编译器放入虚函数表中
-
存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)
说明1:通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。
说明2: 出于效率考虑,没有必要将所有成员函数都声明为虚函数
- 证明vptr的存在
#include <iostream>
using namespace std;
//多态成立的三个条件
//要有继承,虚函数重写,父类指针指向子类对象
class Parent1
{
private:
int a ;
public:
Parent1(int a =0)
{
this->a = a;
}
void print() //1,动手脚 特殊处理 虚函数表
{
cout<<"我是巴巴"<<endl;
}
};
class Parent2
{
private:
int a ;
public:
Parent2(int a =0)
{
this->a = a;
}
virtual void print() //
{
cout<<"我是巴巴"<<endl;
}
};
int main()
{
printf("sizeof(Parent1):%d,sizeof(Parent2):%d\n",sizeof(Parent1),sizeof(Parent2));
cout<<"hello..."<<endl;
return 0;
}
-
vptr指针初始化
对象在创建时,由编译器对VPTR指针进行初始化
只有当对象的构造完全结束后VPTR的指向才最终确定
父类对象的VPTR指向父类虚函数表
子类对象的VPTR指向子类虚函数表