C++之多态
开始新的征程啦———多态,它也是C++的三大特性之一。
文章目录
- 一、多态的概念
- 二、多态的定义和实现
- 2.1多态的定义
- 2.2 实现动态多态所需要的条件(2个)
- 2.3 虚函数的定义
- 2.4 虚函数的重写/覆盖
- 2.5 虚函数重写中的问题
- 2.5.1 协变
- 2.5.2 析构函数的重写
- 2.6 override和final关键字
- 2.6 重载/重写/隐藏
- 三、纯虚函数和抽象类
- 四、多态的原理
- 4.1 虚函数表指针
- 4.2 父子类的虚函数表指针的区分
- 4.3 多态的原理
- 4.4 动态绑定与静态绑定
- 4.4 虚函数表
一、多态的概念
通俗来讲的话,就是多种形态。多态性(Polymorphism) 指的是 “同一接口,不同实现” 的能力。
多态分为编译时多态(静态多态)和运行时多态(动态多态),什么属于静态多态呢?像之前学习的函数重载以及模板(函数模板+类模板)就属于静态多态,为什么叫编译时多态?因为实参传给形参的匹配是在编译时完成的(传不同的参数就可以调用不用的函数,通过参数的不同达到多种形态);什么是运行时多态呢?即:去完成某个行为(函数),传不同的对象,来完成不同的行为,就会达到多种形态。(多态举例:去同一个买票的窗口,普通人去是全价,但是学生是半价,不用的对象,不同的行为)
我们这节讲述的都是动态的多态。
二、多态的定义和实现
2.1多态的定义
动态多态是指在运行时根据对象的实际类型来决定调用哪个函数,与【指针 / 引用的静态类型】无关,不是由这个决定的。它通过虚函数(Virtual Functions) 和继承机制实现,核心是 运行时类型识别 和 虚函数表(VTABLE) 的动态调度。
简单理解就是:一个继承关系下的类类型的对象,去调用同一函数,产生了不同的行为。
2.2 实现动态多态所需要的条件(2个)
非常重要,这个是前提条件
-
被调用的函数必须是虚函数
1.2想让函数有多种形态,首先需要让基类中的那个成员函数成为虚函数
-
必须是基类(父类)指针 / 引用去调用虚函数
【注意注意:
(1)基类基类基类,派生类是没法调用虚函数的哈。而且父类的指针/引用,既可以指向父类对象,又可以指向子类对象(ptr是person(父)类类型的指针,不管你传什么样的派生类过来,他在这里都会进行切割,因此ptr它始终都指向父类的那一部分);
(2)指针或引用,如果是person p;是调用不了的哈,它只是一个单纯的对象,不是指针也不是引用】
为什么必须是基类的指针/引用呢?因为只有基类的指针或引用才能既指向派生类对象,又能指向基类对象。
2.3 虚函数的定义
上述所需的第一个条件是虚函数。那什么是虚函数呢?
在 C++ 中,虚函数是实现动态多态的核心机制。它允许通过 [基类的指针或引用] 调用 [派生类的特定函数] ,而具体调用哪个函数在 运行时 根据对象的实际类型确定,而非编译时.
虚函数的定义:虚函数是在基类中用 virtual 关键字声明的(成员函数),派生类可以 重写(Override) 该函数以提供自己的实现。【非成员函数不能加virtual来修饰,也就是说这个函数是在类里面的】
virtual这个关键字加在成员函数的返回值的前面
class person { public: virtual void Buy() { cout public: virtual void func(int val = 0) //A中的val时0,B中的是1 { std::cout public: void func(int val = 1) { std::cout //先在堆上创建一个 B 类的对象实例,然后将地址赋给A类指针。再用A类指针(基类指针)调用虚函数 //new B 赋值给A* p A* p = new B; //在堆上分配的内存大小为sizeof(B),包含(子类)B自身的成员和从A继承的成员 p-func(); //p是基类。条件之一:用基类的指针去调用虚函数 //输出结果是class B-0 return 0; } std::cout public: virtual void BuyTicket() { cout virtual void BuyTicket() { cout person* p1 = new person; p1-BuyTicket();//买票——全折; person& p2 = *p1; //引用 p2.BuyTicket();//买票——全折; person* s1 = new student; //这里会发生切割关系 s1-BuyTicket();//买票——半折;这里调用的是派生类中的BuyTicket函数。 person& s2 = *s1; s2.BuyTicket();//买票——半折;这里调用的其实也是派生类中的BuyTicket函数。 return 0; } public: virtual ~A() { cout public: virtual ~B() { cout A* a1 = new A; A* a2 = new B; delete a1; delete a2; return 0; } public: virtual void print() final { }//表明print这个虚函数不能构成重写操作 }; public: virtual void drive() = 0; //drive成为了纯虚函数 //所以car类成为了抽象类 }; class xiaomi:public car { //没有对纯虚函数进行重写,所以xiaomi类也是抽象类 }; class xinjie :public car { public: virtual void drive() { cout car* a1 = new car; car* a2 = new xiaomi; car* a3 = new xinjie; return 0; }
-