• c++ 类的多态以及虚函数


    一、 C++ 类对象的内存大小
    接下来看一个例子:

    class animals{
          public:
            animals(){
              std::cout<<"animals"<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    用该类实例化一个对象,看一下对象的大小:
    [图片]

    通过测试程序显示这个对象1bytes,这里我就很纳闷?这个类的什么成员占有了他的一个字节?
    网上搜索的资料,类的成员函数,静态成员,this指针都是不占内存的,那么为什么这个类是1bytes。
    空类的大小为1byte:
    原因是为了让对象的实例能够相互区别。空类同样能够被实例化,并且每个实例在内存中都有独一无二的地址,因此,编译器会给空类隐含加一个字节,这样空类实例化之后就会拥有独一无二的内存地址。
    如果没有这一个字节的占位,那么空类就无所谓实例化了,因为实例化的过程就是在内存中分配一块地址。
    当空类作为另一个类的成员时,大小计算在内。但是当一个类继承空类时:大小不计算,空白基类最优化(EBO/EBCO)
    那么上述例子就应该相当于空类。现在我们加上虚函数看一下他的大小。
    [图片]

    上述显示占8个字节,说明加上虚函数之后,生成了新的函数成员且占用内存大小为8 byte,这个成员函数应该就是我们经常说的虚函数指针。因为我用的是64位操作系统,所以他是8byte,当使用32位时,他应该是4byte。
    那么通过上述现象就可以解释很多面试时常问的问题,例如在虚函数指针什么时候被生成的,在编译期,还是程序运行的时候?
    这里就可以看出虚函数指针是在创建对象的时候,执行了构造函数,虚函数指针才被初始化,而并不是存在于类中的。
    那么虚函数指针是干什么用的?
    虚函数指针是用来找虚函数表的,虚函数表里存放了虚函数的地址,他可以通过虚函数表找到虚函数的地址,从而调用虚函数。
    虚函数表的构建是在编译期,在编译期就构建好了虚函数表,当生成对象执行构造函数时,将虚函数表的首地址赋给虚函数指针,完成虚函数指针的初始化。
    二、通过虚函数机制实现类的多态
    继承机制,相信大家都学过,据个例子:

    class animals{
          public:
            animals(){
              std::cout<<"animals"<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    我们还是先从内存的机制来看,我们看一下 dog的内存大小:先来new 一下
    dog * dg = new dog()
    在这里插入图片描述
    我们现在在基类加一些变量,例如 long weights = 50,现在dog的内存大小:
    [图片]

    这个说明继承机制,会开为父类,以及祖父类的类得空间都会开出来,除了空类。
    我们现在再在这个基础上为父类得 cry()函数加上 virtual关键字,让其生成虚函数指针,得到内存大小为16 bytes。
    [图片]

    现在我们再为子类加一个virtual 关键字,看会不会生成一个属于子类的虚函数指针。随便给一个函数添加一个。
    [图片]

    说明子类共享基类的虚函数指针。
    那么接下来我们看一个典型的多态的例子:

    #include
    #include
    #include
    class animals{
          public:
            animals(){
              std::cout<<"animals"<cry();
        an->run();
        delete an;
        animals * bn = new animals();
        bn->cry();*/
        animal->cry();
        delete animal;
        /*animals am = animals();
        std::cout << sizeof(animals) << std::endl;
        std::cout << "animals obj size = " << sizeof(am) <<" bytes" << std::endl;
        */
        return 0;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    这个典型案例:通过基类定义一个animals 类型的指针,test()实例化一个对象, 用animals类型的指针指向了test实例化的对象,同上,这个操作可以使animal 通过虚函数指针调用test()的虚函数cry(),呢么是如何实现的呢?
    虚函数表的生成是在编译阶段,呢么这个继承机制会把虚函数加入一个表,这个表称为虚函数表,虚函数指针的首地址就指向这个表,呢么他是如何找到test cry()的呢,这里是用了一个偏移量来进行寻址的,根据test对象里cry函数的偏移量 + 虚函数指针的首地址,就是我们要找到的函数位置。由于我们已经证明过了,继承的对象中所有函数共享虚函数指针,所以类的多态大概率就是这么来的。

  • 相关阅读:
    满意度从50%到90%,客服系统是怎么做到的
    【限时免费】20天拿下华为OD笔试之 【前缀和】2023B-最大子矩阵和【欧弟算法】全网注释最详细分类最全的华为OD真题题解
    普中51-数码管实验
    跨境电商年底风控升级,测评养号如何选择稳定且纯净的IP环境?
    计算机网络——数据链路层
    使用Python的Turtle库绘制一个心形图像(含详细Python代码与注释)
    Python 自动化(十五)请求和响应
    从内核世界透视 mmap 内存映射的本质(源码实现篇)
    智能售后工单系统是什么?智能工单系统有什么用?
    大一新生如何选电脑?专业不同,需求不同
  • 原文地址:https://blog.csdn.net/yangzijiangac/article/details/126779671