复习内容
- 什么是多态?(1)静多态:编译时期的多态,有被称为早绑定 代表:函数重载,模板(2)动多态:运行时期的多态,又被称为晚绑定 代表:继承中的多态
- 动多态的产生条件:指针或引用调用虚函数+对象必须完整
- 完整对象:构造函数执行完毕,析构函数还没开始
class Base
{
public:
Base()
{
this->fun();
}
virtual void fun()
{
cout << "Base::virtual void fun()" <<endl;
}
};
class Derive:public Base
{
public:
Derive()
{
this->fun();
}
virtual void fun()
{
cout << "Derive::virtual void fun()" <<endl;
}
}
int main()
{
Derive d; //不是完整对象
return 0;
}
- 构造函数能不能写成虚函数?不能,(1)构造函数无法通过指针或者引用调用(2)vfptr是在构造时候写入对象,而多态调用虚函数需要用到vfptr
- 什么情况下析构函数必须写成虚函数?存在父类指针指向堆上的子类对象时,就必须把父类的析构函数写成虚函数(自己疑惑的一个问题,既然析构的父类的指针指向的是子类的析构函数,因为指针是指向父类的,所以会调用父类的析构函数,因为父类的虚构函数是虚函数,所以去虚函数表当中找,不是就可以找到父类的析构函数吗?----------因为子类在继承的时候,继承了父类的虚函数表,并且对相同的函数进行了覆盖,所以找到的是子类的析构函数。)
- Derive* dp = new Base();//先在堆上申请空间,然后调用构造函数进行构造,如果子类对象中有虚函数,而父类空间中没有虚函数,释放delete dp时则会报错。所以父类指针指向堆上的子类对象时候,一定要确保父类中有虚函数。
- vftable什么时候产生?在哪里存储?在编译时期产生的,在.rodata段存储
- 类的编译顺序:(1)先编译类名(2)再编译成员名(3)最后编译成员函数函数体
- 虚函数能不能被处理成内联?不能,虚函数需要将函数指针放到vftable,而内联函数在编译期展开,在release版本没有地址---------不太懂
- 静态函数能不能被写成虚函数?不能,静态函数不依赖于对象,无法产生动多态。
RTTI
- 全称:run time type info
- RTTI实际上是一个指针,指向一个保存类信息的结构体type
int main()
{
Base* p = new Derive();
cout << typeid(p).name << endl; //输出class Base*
cout << typeid(*p).name << endl;//输出class Derive,在这里调用会用到RTTI,产生多态
delete p;
return 0;
}
- RTTI在编译时期产生,type结构体中的信息不可改,所以放在只读.rodata数据段
RTTI的作用
- dynamic_cast:父类指针转为子类指针专用的类型强转,要求: (1)必须有RTTI (2)父类指针指向的对象中的RTTI确实是子类的
int main()
{
Base* p = new Derive();
cout << typeid(p).name << endl;
cout << typeid(*p).name << endl;
Derive* pd = (Derive*)p;//C语言强转,可以让pd指向子类的指针,但是这种方法并不安全,因为并不确定p原来指向的是否为子类对象,所以可以用底下的方法
Derive* pd = dynamic_cast<Derive*>p;//这也是一种强转方法,但是它在强转之前会检查,p是不是尖括号里边的类型
//如果不是,程序会崩溃
delete p;
return 0;
}