祗疼妳一个 发表于 4 小时前

深入明确C++面向对象特性之一 多态

欢迎来到干货小堆栈,堪比戈壁!!!

从“Hello World”到改变天下,中隔断着千万次'再试一次'.

https://i-blog.csdnimg.cn/direct/0d81c0a2a5ab4163ba169e1b5c969ef1.png
1.多态的概念

多态的概念:普通来说,就是多种形态,具体点就是去完成某个举动,当差别的对象去完成时会产生出差别的状态。比方:在买火车票时,如果平常人买票时,是全价买票;门生买票时,是半价买票;武士买票时,是优先买票。
2.多态的界说及实现

2.1多态的构成条件

多态是在差别继续关系的类对象,去调用同一函数,产生差别的举动。比如Student继续了Person。Person对象买全价票,Student对象买半价票。
示例:
https://i-blog.csdnimg.cn/direct/e91ac09955da4eafbc78acbc55f0edf3.png
那么在继续中要构成多态的条件有两个条件:
1、必须通过基类的指针或引用调用虚函数。
2、被调用的函数必须是虚函数,且派生类必须对基类的虚函数举行重写。
2.2虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数。

class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl;}
};2.3虚函数的重写

条件
①是虚函数
②函数名、返回值和参数范例必须雷同
但是有两个例外:
1.协变(基类和派生类的虚函数返回值范例可以差别),但是要求返回值范例必须是父子关系的指针和引用。
2.派生类的重写虚函数可以不加 virtual,基类的重写虚函数必须加上。(发起都加上)
示例:返回值的差别

class A
{ };
class B:public A
{ };

class Person
{
public:
        virtual const A& Buyticket()
        {
                cout << "买票全价" << endl;
                return A();
        }
};
class Student :public Person
{
public:
        virtual const B& Buyticket()
        {
                cout << "买票半价" << endl;
                return B();
        }
};2.4重载、重写(覆盖)和重界说(匿伏)的对比

https://i-blog.csdnimg.cn/direct/5b6bb1c39bdb45138931221c3fb9208d.png
3.C++ 11 override 和 final 关键字

3.1 final

作用:
①修饰虚函数,使该虚函数不能被重写。
②修饰类,使该类不能被继续。
示例:
https://i-blog.csdnimg.cn/direct/81b1a8c7b77f49419c3d4aafbd8432a9.png
3.2 override

作用:资助派生类查抄是否对虚函数完成重写,没有重写会报错。
https://i-blog.csdnimg.cn/direct/0d1ed735bd784a47943584e2002bfd8e.png
4.抽象类

在虚函数的反面写上 =0,则这个函数为 纯虚函数。
包罗纯虚函数的类叫做 抽象类(接口类).
https://i-blog.csdnimg.cn/direct/3c5cc3e259454f64b904e4032fb97508.png
4.1虚函数的重写(接口继续)

示例:以下步调的输出效果是什么?
  A: A->0     B: B->1    C: A->1    D: B->0     E: 编译堕落    F: 以上都禁绝确
class A
  {
  public:
      virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
       virtual void test(){ func();}
  };
 
  class B : public A
  {
  public:
      void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }
  };
 
  int main()
  {
      B*p = new B;
      p->test();
      return 0;
  }准确答案:B
分析:构成虚函数的重写,调用的是B对象对重写的虚函数,但是 虚函数重写的是实现,参数都是基类的那部分(接口继续)。变形:https://i-blog.csdnimg.cn/direct/1f9b91c3ad2542f08975616801743c09.png答案:D4.2内联函数、静态成员函数和构造函数是否可以是虚函数?

https://i-blog.csdnimg.cn/direct/5434c3c4ffb84e5882c6ad4057e0c51c.png
5.多态的底层原理

5.1虚函数表

示例:以下 sizeof(Base) 是多少?


class Base
{
public:
virtual void Func1()
{
cout << "Func1()" << endl;
}
private:
int _b = 1;
};分析:通过编译得到 sizeof(Base) 占 8个字节,多存了一个指针(指向虚函数表)。
https://i-blog.csdnimg.cn/direct/a98922c20db147d98ce3f0860e2c34d4.png
留意:对基类的虚函数举行了重写,其对应的虚函数表指针指向的地点也会发生厘革。
1、基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,以是d的虚表中存的是重写的Derive::Func1,以是虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。
示例:
https://i-blog.csdnimg.cn/direct/445f071fea374dceaea773e7f1636928.png
2、虚函数表本质是一个存虚函数指针的指针数组,一样平常情况这个数组最反面放了一个nullptr。
3、总结一下派生类的虚表天生:
a.先将基类中的虚表内容拷贝一份到派生类虚表中。
b.假如派生类重写了基类中某个虚函数,用派生类本身的虚函数覆盖虚表中基类的虚函数。
c.派生类本身新增长的虚函数按其在派生类中的声明序次增长到派生类虚表的末了。
示例:
https://i-blog.csdnimg.cn/direct/93e8ce1a63b546989943aee5f1c67349.png
4、派生类对基类的虚函数重写了,故派生类产生的对象,共用一份虚表。
示例:
https://i-blog.csdnimg.cn/direct/f5935a20d3e649efa9fb1011ba020589.png
5.2虚函数表,存在那边?

对比验证法:根据多态的存储模子,该对象的前面四个字节存放的是虚表的地点,分别与栈、静态区、堆、常量区对比。
https://i-blog.csdnimg.cn/direct/dde38ca0add843ef8f4e31e3559a8894.png
5.3动态绑定与静态绑定

1. 静态绑定又称为前期绑定(早绑定),在步调编译期间确定了步调的举动,也称为静态多态,比如:函数重载
2. 动态绑定又称后期绑定(晚绑定),是在步调运行期间,根据具体拿到的范例确定步调的具体举动,调用具体的函数,也称为动态多态。
https://i-blog.csdnimg.cn/direct/3a755537600643d08bea7cab8e904992.png
6.多继续的多态

6.1多继续的虚函数表

观察下图可以看出:多继续派生类的为重写的虚函数放在第一个继续基类部分的虚函数表中
https://i-blog.csdnimg.cn/direct/0b92b2407be24031bae917394f755b02.png
为什么重写func1(),Base1 和 Base2 的虚表中的地点不一样,但效果一样?
必要从底层的汇编举行深入刨析,根据其会变得调用,分析得到 编译器做了处置惩罚,目标是为了修改 this 指针。
6.2菱形假造继续的多态

https://i-blog.csdnimg.cn/direct/7624298d28984c8daf80d3755a27a759.png
https://i-blog.csdnimg.cn/direct/efc8231c73d643cb81c43554dfef2e74.png

以为不错的可以点赞+收藏咯!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 深入明确C++面向对象特性之一 多态