zoukankan      html  css  js  c++  java
  • 孙鑫视频笔记之——深入理解多态是如何实现的

    废话不多说,直接上程序:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class Animal
     5 {
     6 public:
     7     Animal()
     8     {
     9         cout << "我是一个类" << endl;
    10     }
    11     void eat()
    12     {
    13         cout << "Animal eat!!!" << endl;
    14     }
    15 };
    16 
    17 
    18 
    19 class Dog : public Animal
    20 {
    21 public:
    22     void eat()
    23     {
    24         cout << "狗吃屎!" << endl;
    25     }
    26 
    27 };
    28 
    29 
    30 int main()
    31 {
    32     Animal* pAnimal;
    33     Dog dog;
    34     pAnimal = &dog;//父类的指针指向子类地址,或者说子类地址转化为父类指针
    35     pAnimal->eat();
    36     return 0;
    37 }

    首先Animal他是一个父类。有一个函数叫eat();

    Dog是Animal的子类,并且重写了eat(),(这就是所谓的函数的覆盖,ps:不是重点,呵呵)。

    现在,我们想看看父类如何调用子类的eat();这是实现多态的根本。

    Animal* pAnimal;   //声明一个父类的指针
    Dog dog;                 //创建一个子类的对象
    pAnimal = &dog;  //父类的指针指向子类地址,或者说子类地址转化为父类指针

    这里,不知道您会不会产生一个疑惑,“为什么转换的这么的理所当然?”

    要知道Animal和Dog 是不同的类,就像int 和 float也不是随随便便可以转的,

    搞不好就是精度丢失,您说是不是。那这里将父类的指针储存子类的地址会不会产生什么副作用呢?答案是否定的。这就不得不说说内存模型了!两个类能否相互转化,就得看他们的内存模型是否匹配。我们看看父类和子类的内存模型是怎样的:

    image

    我们知道每当创建子类的对象的时候,子类都会调用父类的构造函数,

    从而构建父类,(可以理解为,将老爸的基因原原本本的继承下来)。那我们看到

    Dog对象的内存分布如上图所示,可以看成老爸的基因在上面,自己的“突变”(基因突变了,哈哈)在下面。

    再来看这么一句

    pAnimal = &dog;  //父类的指针指向子类地址,或者说子类地址转化为父类指针

    因为父类和子类的内存模型是一样的,所以这样转换是没有问题的。

    pAnimal->eat();那么这么写,父类能调用到子类的eat()吗?

    看看Dog的内存模型,就知道了,其实此时pAnimal->eat();依然调用的是父类的eat();

    打印出的是:

    image

    可惜啊,没有打印出“狗吃屎!”真是可惜了,不急,待哥哥加上一个关键字就万事大吉了!

    及在父类的函数eat()前加一个virtual 即可,程序如下:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class Animal
     5 {
     6 public:
     7     Animal()
     8     {
     9         cout << "我是一个类" << endl;
    10     }
    11     virtual void eat()
    12     {
    13         cout << "Animal eat!!!" << endl;
    14     }
    15 };
    16 
    17 
    18 
    19 class Dog : public Animal
    20 {
    21 public:
    22     void eat()
    23     {
    24         cout << "狗吃屎!" << endl;
    25     }
    26 
    27 };
    28 
    29 
    30 int main()
    31 {
    32     Animal* pAnimal;
    33     Dog dog;
    34     pAnimal = &dog;//父类的指针指向子类地址,或者说子类地址转化为父类指针
    35     pAnimal->eat();
    36     return 0;
    37 }

    image

    哈哈,这回爽了吧,啊哈哈~~

    我们看到仅仅加上一个virtual多态的性质就出来了 ,这是因为,加上virtual之后,C++就会

    采用迟绑定技术,而不是在一开始编译的时候就确定调用哪个函数,而是在运行的时候去判断

    调用哪个函数,如果子类拥有该函数,则使用子类的,如果子类没有实现(重写)这个函数,

    那么还是调用父类的。还有一点值得注意,由于父类的eat是虚函数,子类继承了父类,即使

    没有加virtual 子类的eat()也是虚函数!

        说完虚函数,再讲纯虚函数,这两个我觉得容易混淆,呵呵,一起来分析下:

    将父类的eat()稍加改动:virtual void eat() = 0;这个区别是,函数没有实现。

    那么这个就是纯虚函数。拥有纯虚函数的类,被称之为抽象类。抽象类无法创建

    实例对象。但是可以被继承,继承他的类,要想实例化对象,必须去实现这个

    虚函数。如果不实现,那么该子类同样无法实例化对象,因为此时它也是抽象类了。

       例子就不再举了,自己去试试吧~~

  • 相关阅读:
    js实现打字机效果(完整实例)
    纯css高斯背景模糊(毛玻璃,伪元素,完整实例)
    vue首次缓存判断
    vue使用bus.js在兄弟组件传值
    叶子节点和tensor的requires_grad参数
    Mysql 8.x初次安装过程中遇到MySQL 服务无法启动的解决方法
    Apriori算法介绍(Python实现)
    springBoot单参数校验全局异常抛出
    解决vue 动态添加标签给标签添加自定义方法显示not function问题
    macbook pro、gitlab、SourceTree提交代码
  • 原文地址:https://www.cnblogs.com/douzi2/p/3843407.html
Copyright © 2011-2022 走看看