多态简单来说就是多种形态
同一个行为,不同对象去完成时
会产生出不同的状态
多态分为静态多态
和动态多态
静态多态指的是编译时
在程序编译期间确定了程序的行为
比如:函数重载
动态多态指的是运行时
在程序运行期间,根据具体拿到的类型
确定程序的具体行为,调用具体的函数
必须通过父类指针或引用调用虚函数
虚函数的重写
函数名、参数类型、返回值都要相同
被virtual修饰的类成员函数称为虚函数
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
派生类中有一个跟基类完全相同的虚函数
(即派生类虚函数与基类虚函数的返回值类
型、函数名字、参数列表完全相同)
称子类的虚函数重写了基类的虚函数
普通函数的继承是实现继承
派生类继承了基类函数,可以使用函数
继承的是函数的实现
虚函数的继承是接口继承
派生类继承的是基类虚函数的接口
目的是为了重写,达成多态,继承的是接口
如果不实现多态,不要把函数定义成虚函数
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
// 只要父类析构加了virtual就构成多态,子类加不加都可以正确释放
virtual ~Person() { cout << "~Person" << endl; };
};
class Student : public Person {
public:
// 子类可以不写virtual,因为他继承父类的接口,重写实现
virtual void BuyTicket() { cout << "买票-半价" << endl; }
~Student() { cout << "~Student" << endl; }
};
void Func(Person& p)
{ p.BuyTicket(); }
int main()
{
Person ps;
Student st;
// 构成多态后
Func(ps); // 传父类调用父类的虚函数
Func(st); // 传子类调用子类的虚函数
return 0;
}
如果是父子关系的指针或引用
返回值可以不同也构成多态
class A{};
class B : public A {};
class Person {
public:
virtual A* f() {return new A;}
};
class Student : public Person {
public:
virtual B* f() {return new B;}
};
final: 修饰虚函数
表示该虚函数不能再被重写
现实中不常用,不能实现多态的虚函数
意义不大
class Car
{
public:
virtual void Drive() final {}
};
class Benz :public Car
{
public:
virtual void Drive() {cout << "Benz-舒适" << endl;}
};
override: 检查派生类虚函数
是否重写了基类某个虚函数
如果没有重写编译报错
class Car{
public:
virtual void Drive(){}
};
class Benz :public Car {
public:
virtual void Drive() override {cout << "Benz-舒适" << endl;}
};
面试题经常被问到
在虚函数后面加上 =0
这个函数就叫纯虚函数
包含纯虚函数的类叫做抽象类
抽象类不能实例化出对象
派生类继承后也不能实例化出对象
只有重写纯虚函数,派生类才能实例化出对象
class Car
{
public:
// 纯虚函数 --- 抽象类
virtual void Drive() = 0;
};
int main()
{
Car car; // 无法实例化对象
return 0;
}
这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:
virtual void Func1()
{
cout << "Func1()" << endl;
}
private:
int _b = 1;
};
int main()
{
cout << sizeof(Base) << endl;
return 0;
}
在32位操作系统下是8 bit
在64位操作系统下是16 bit
通过调试发现还有个指针_vfptr
这个指针叫做虚函数表指针
本质是指针数组
用来存放虚函数的地址
通过下面代码观察父子类
的虚表之间的关系
class Base
{
public:
virtual void Func1()
{
cout << "Base::Func1()" << endl;
}
virtual void Func2()
{
cout << "Base::Func2()" << endl;
}
void Func3()
{
cout << "Base::Func3()" << endl;
}
private:
int _b = 1;
};
class Derive : public Base
{
public:
virtual void Func1()
{
cout << "Derive::Func1()" << endl;
}
private:
int _d = 2;
};
int main()
{
Base b;
Derive d;
return 0;
}
监视窗口
通过监视窗口可以发现
派生类对象d中也有一个虚表指针
证明基类和派生类的虚表是不一样的
结论:
虚函数表本质是存虚函数指针的指针数组
一般情况这个数组最后面放了一个nullptr
对象中存的是虚表指针
虚表存的是虚函数指针
虚函数和普通函数一样的,都是存在代码段的
派生类的虚表生成:
a.
先将基类中的虚表内容拷贝一份到派生类虚表中
b.
如果派生类重写了基类中某个虚函数
用派生类自己的虚函数覆盖虚表中基类的虚函数
c.
派生类自己新增加的虚函数按其在派生类中的
声明次序增加到派生类虚表的最后
????????
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见
????????