淄博手机网站建设,极构网站建设工作室,google关键词查询工具,国内地铁建设公司网站一、多态的概念及定义
1.1 多态的概念
多态简单来说就是多种形态 同一个行为#xff0c;不同对象去完成时 会产生出不同的状态 多态分为静态多态和动态多态 静态多态指的是编译时 在程序编译期间确定了程序的行为 比如#xff1a;函数重载 动态多态指的是运行时 在程序运行…
一、多态的概念及定义
1.1 多态的概念
多态简单来说就是多种形态 同一个行为不同对象去完成时 会产生出不同的状态 多态分为静态多态和动态多态 静态多态指的是编译时 在程序编译期间确定了程序的行为 比如函数重载 动态多态指的是运行时 在程序运行期间根据具体拿到的类型 确定程序的具体行为调用具体的函数
1.2 在继承中要构成多态的两个条件 必须通过父类指针或引用调用虚函数 虚函数的重写 函数名、参数类型、返回值都要相同
被virtual修饰的类成员函数称为虚函数
class Person {
public:virtual void BuyTicket() { cout 买票-全价 endl;}
};1.3 虚函数的重写(覆盖) 派生类中有一个跟基类完全相同的虚函数 (即派生类虚函数与基类虚函数的返回值类 型、函数名字、参数列表完全相同) 称子类的虚函数重写了基类的虚函数 普通函数的继承是实现继承 派生类继承了基类函数可以使用函数 继承的是函数的实现 虚函数的继承是接口继承 派生类继承的是基类虚函数的接口 目的是为了重写达成多态继承的是接口 如果不实现多态不要把函数定义成虚函数 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;
}1.4 协变 如果是父子关系的指针或引用 返回值可以不同也构成多态 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;}
};1.5 final和override
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;}
};1.6 重载、覆盖(重写)、隐藏(重定义)的对比
面试题经常被问到
1.7 抽象类 在虚函数后面加上 0 这个函数就叫纯虚函数 包含纯虚函数的类叫做抽象类 抽象类不能实例化出对象 派生类继承后也不能实例化出对象 只有重写纯虚函数派生类才能实例化出对象 class Car
{
public:// 纯虚函数 --- 抽象类virtual void Drive() 0;
};int main()
{Car car; // 无法实例化对象return 0;
}二、多态的原理
2.1 虚函数表
这里常考一道笔试题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 这个指针叫做虚函数表指针 本质是指针数组 用来存放虚函数的地址 对象中存的是虚表指针 虚表存的是虚函数指针 虚函数和普通函数一样的 都是存在代码段的 2.2 多态的原理
通过下面代码观察父子类 的虚表之间的关系
class Base
{
public:virtual void Func(){cout Base::Func1() endl;}
};
class Derive : public Base
{
public:virtual void Func(){cout Derive::Func1() endl;}
};void Test(Base* p)
{p-Func();
}int main()
{Base b;Derive d;Test(b);return 0;
}监视窗口 通过监视窗口可以发现 派生类对象d中也有一个虚表指针 通过地址发现基类和派生类的虚表是不一样的
虚函数表本质是存虚函数指针的指针数组 一般情况这个数组最后面放了一个nullptr
结论 观察下图红色箭头 当传过来的是父类对象的地址 p-Func在父类的虚表中找对应的虚函数Func地址 当传过来的是子类对象的地址 p-Func在子类的虚表中找对应的虚函数Func地址
这样就实现不同对象的同一行为 展现的不同状态 当父类有虚函数而子类没有虚函数 也没有重名函数 子类是不能继承父类的虚表指针 子类会生成一个虚表指针 存父类的虚函数地址 当子类有虚函数而父类没有 父类也就不会有虚表 因为子类有的东西父类不一定有
派生类的虚表生成 a.先将基类中的虚表内容拷贝一份到派生类虚表中 b.如果派生类重写了基类中某个虚函数 用派生类自己的虚函数覆盖虚表中基类的虚函数 c.派生类自己新增加的虚函数按其在派生类中的 声明次序增加到派生类虚表的最后
2.3 在多态下建议把基类的析构函数定义成虚函数 如果基类析构函数不是虚函数 就调不到派生类的析构函数 (指针类型是父类所以调用父类的析构函数) 从而造成内存泄漏 class Person {
public:virtual ~Person() {cout ~Person() endl;}
};
class Student : public Person {
public:virtual ~Student() { cout ~Student() endl; }
};int main()
{Person* ps new Student;delete ps;return 0;
}
形成多态的条件之一便是 只能通过父类去调用 所以子类对象只能强转成父类类型 如果父类的析构函数不是虚函数 那子类便调不到自己的析构函数 因为子类对象的类型是父类 所以只能调用父类的析构函数 子类成员无法释放从而造成内存泄漏 父类析构函数定义成虚函数便能解决问题
✨✨✨✨✨✨✨✨ 本篇博客完感谢阅读 如有错误之处可评论指出 博主会耐心听取每条意见 ✨✨✨✨✨✨✨✨