• C++中多态的原理【精华】


    虚函数表

    通过一道题我们先感受一下编译器针对多态的处理

    #include 
    using namespace std;
    
    class Base
    {
    public:
    	virtual void Func1()
    	{
    		cout << "Func1()" << endl;
    	}
    private:
    	int _b = 1;
    	char _c
    };
    
    int main()
    {
    	cout << sizeof(Base) << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在x64环境下,显示的结果是12字节
    在这里插入图片描述

    为什么是12字节呢?一个int就4字节,一个char就1字节,内存对齐一下也是8字节呀!为什么显示的结果是12字节呢? 我们调试代码,看看内部结构
    在这里插入图片描述
    通过调试我们发现Base创建的对象里面有一个指针_vfptr,如果我们计算sizeof的时候把这个指针算上的话刚刚好的12字节!

    那么疑问来了,这个指针到底是什么?为什么会出现在对象里面?这个指针到底有什么作用?

    这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表,。那么子类中这个表放了些什么呢?我们接着往下分析

    #include 
    using namespace std;
    
    class Base
    {
    public:
    	virtual void Func1()
    	{
    		cout << "Func1()" << endl;
    	}
    
    	virtual void Func2()
    	{
    		cout << "Func2()" << endl;
    	}
    
    	void Func3()
    	{
    		cout << "Func3()" << endl;
    	}
    private:
    	int _b = 1;
    	char _c;
    };
    
    int main()
    {
    	cout << sizeof(Base) << endl;
    	Base b;
    	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

    通过监视窗口再来看:
    在这里插入图片描述
    我们发现这个指针指向一个数组(虚函数表),这个数组(表)里面存的就是虚函数的地址


    知道了包含虚函数的类里面会出现一个指针,这个指针指向一个指针数组,数组里面保存的是类里面的虚函数,接下来我们看看多态的原理


    多态的原理

    提示:这里可以添加本文要记录的大概内容:

    class Person {
    public:
     virtual void BuyTicket() { cout << "买票-全价" << endl; }
     virtual void func(){}
    private:
    	int _a = 1; 
    };
    
    class Student : public Person {
    public:
     virtual void BuyTicket() { cout << "买票-半价" << endl; }
    private:
    	int _b = 0; 
    };
    
    void Func(Person& p)
    {
    	p.BuyTicket();
    }
    
    int main()
    {
    	Person p;
    	Student s;
    	Func(p);
    	Func(s);
     	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

    我们看一下父类的虚表指针
    在这里插入图片描述
    我们看到Person对象里面的_vfptr指向的指针数组里面有两个指针,说明保存的是两个地址,我们看看这两个地址是什么?
    在这里插入图片描述
    然后我们看一下_vfptr指针指向的数组里面保存的地址
    在这里插入图片描述
    再看一下子类的虚表指针
    在这里插入图片描述
    虚函数需要被重写,一开始子类会将虚函数进行直接拷贝,重写虚函数之后,虚函数的地址就发生改变了

    那为什么多态的原理和指向的对象有关?和虚表有什么关系?

    在这里插入图片描述

    父类对象找到虚函数表的地址,在表里面找到虚函数的地址,因为这里是父类对象,直接调用父类对象的虚函数
    子类对象赋值给父类对象,这样会产生“切片”,找到子类中父类的那一部分,寻找到虚函数表的地址,但是虚函数表里存的是子类重写的父类虚函数的地址,所以此时_vfptr指向的其实是子类的虚函数指针数组,自认而然掉用的是子类的虚函数

    那为什么不使用指针和引用后,就不构成多态了呢?

    #include 
    #include 
    #include 
    using namespace std;
    
    class Person {
    public:
    	virtual void BuyTicket() { cout << "买票-全价" << endl; }
    	virtual void func() {}
    private:
    	int _a = 1;
    };
    
    class Student : public Person {
    public:
    	virtual void BuyTicket() { cout << "买票-半价" << endl; }
    private:
    	int _b = 0;
    };
    
    void Func(Person p)
    {
    	p.BuyTicket();
    }
    
    int main()
    {
    	Person p;
    	Student s;
    	Func(p);
    	Func(s);
    	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

    在这里插入图片描述

    因为指针和引用是指向子类对象切割出来的父类对象的那一部分
    不使用指针和引用后,直接将子类传递给父类,切割出来的子类对象中父类的那一部分,成员拷贝给父类,但是不会拷贝虚表指针

  • 相关阅读:
    【Java 进阶 - 目录】
    猿辅导创新教育研究院解读新课标:三大变化要注意
    3.Tensors For Beginners- Forward and Backward Transformations
    flutter系列之:flutter中常用的ListView layout详解
    第六章第二节:图的遍历(广度优先遍历和深度优先遍历)和应用(最小生成树、最短路径、有向无环图的描述表达式、拓扑排序、关键路径)
    【毕业设计】33-基于单片机的直流电机的转速检测与控制设计(原理图工程+PCB工程+源代码工程+仿真工程+答辩论文)
    vCenter7.0.0升级到vCenter7.0u3h
    c++中的宏#define用途
    深入浅出理解分布式一致性Paxos算法
    5. web信息收集(OWASP实战训练)
  • 原文地址:https://blog.csdn.net/weixin_65660590/article/details/133937069