如果需要通过基类的指针指向派生类的对象,并且访问访问某个和基类同名的成员,就需要在基类中将同名函数声明为虚函数。从而实现多态。
语法形式:
virtual 函数类型 函数名(形参表);
1.虚函数声明只能出现在类定义中的函数原型声明中
2.派生类中的虚函数会隐藏基类中同名函数的所有重载形式
#include
using namespace std;
class base1
{
public:
virtual void show(){cout<<"base1 is constructing"<show(); //如若改成 ptr->base2::show(),则无论ptr指向的类是什么,都会调用base2的show函数
}
int main()
{
base1 b1;
base2 b2;
fun(&b1);
fun(&b2);
return 0;
}
运行结果
base1 is constructing
base2 is constructing
如果有可能通过基类指针调用对象的析构函数(通过delete),就需要让基类的析构函数成为虚函数。
#include
using namespace std;
class base
{
public:
virtual ~base(){cout<<"desructing base"<
运行结果
destructing derived//根据7.4,先调用派生类析构函数,再调用基类析构函数
desructing base
声明格式:
virtual 函数类型 函数名(参数表)=0;
声明为纯虚函数后,基类中可以不再给出函数的实现部分
注意:纯虚函数≠函数体为空的虚函数!
带有纯虚函数的类是抽象类。抽象类不能实例化,即不能定义一个抽象类的对象,只能定义其指针和引用。
#include
using namespace std;
class base1
{
public:
virtual void show() =0;//纯虚函数定义
};
class base2:public base1
{
public:
void show(){cout<<"base2 is constructing"<show();
}
int main()
{
base2 b2;
derived d1;
fun(&b2);
fun(&d1);
return 0;
}
运行结果
base2 is constructing
derived is constructing
多态类型是指有虚函数的类类型。多态类型的析构函数建议使用虚函数。
dynamic_cast可以将基类指针显式转换为派生类指针,并且会在转换前检查指针所指向对象的类型与转换的目的类型是否兼容,若兼容则进行转换,不兼容则会得到一个空指针。且转换前类型必须是是指向多态类型的指针。
#include
#include //assert断言的头文件
using namespace std;
// 我是父类
class Tfather
{
public:
virtual void f() { cout << "father's f()" << endl; }
};
// 我是子类
class Tson : public Tfather
{
public:
void f() { cout << "son's f()" << endl; }
int data; // 我是子类独有成员
};
int main()
{
Tfather father;
Tson son;
son.data = 123;
Tfather *pf;
Tson *ps;
//子到父
/* 上行转换:没有问题,多态有效(参考7中类型兼容规则) */
ps = &son;
pf = dynamic_cast(ps);
pf->f();
//子到子
/* 下行转换(pf实际指向子类对象):没有问题 */
pf = &son;
ps = dynamic_cast(pf);
ps->f();
cout << ps->data << endl; // 访问子类独有成员有效
//父到子,因为子类新增了成员,所以会失败,返回一个空指针
/* 下行转换(pf实际指向父类对象):含有不安全操作,dynamic_cast发挥作用返回NULL */
pf = &father;
ps = dynamic_cast(pf);
assert(ps != NULL); // 违背断言,阻止以下不安全操作
ps->f();
cout << ps->data << endl; // 不安全操作,对象实例根本没有data成员
return 0;
}
运行结果
son's f()
son's f()
123
Assertion failed: (ps != NULL), function main, file tempCodeRunnerFile.cpp, line 46.
zsh: abort "/var/folders/45/m9lm0lfj5zj133tbhrqfqb_80000gn/T/"tempCodeRunnerFile
1.assert()函数
若括号内表达式值为1(条件成立),assert不执行任何操作;
若括号内表达式值为0(条件不成立),assert函数会打印一条错误信息,并中止程序。
2.基类到派生类的显式转换
若派生类新增了成员,便会转换失败。
typeid用于获取一个类型的相关信息,通过typeid得到一个type_info类型的常引用
语法形式:
typeid(表达式) 或 typeid(类型说明符)
#include
#include
using namespace std;
class base
{
public:
virtual ~base(){}
};
class derived:public base
{
};
void fun(base *b)
{
const type_info &info1=typeid(b);
const type_info &info2=typeid(*b);
cout<<"typeid(b):"<
运行结果
typeid(b):P4base
typeid(*b):4base
This is a base class!
typeid(b):P4base
typeid(*b):7derived