• 多态原理探究-从C++编译器角度理解多态的实现原理


    理论知识:

    当类中声明虚函数时,编译器会在类中生成一个虚函数表。

    虚函数表是一个存储类成员函数指针的数据结构。

    虚函数表是由编译器自动生成与维护的。

    virtual成员函数会被编译器放入虚函数表中。

    当存在虚函数时,每个对象中都有一个指向虚函数表的指针(C++编译器给父类对象、子类对象提前布局vptr指针;当进行howToPrint(Parent *base)函数是,C++编译器不需要区分子类对象或者父类对象,只需要再base指针中,找vptr指针即可。)。

    VPTR一般作为类对象的第一个成员。


    多态的实现原理




    说明1:

    通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。

    说明2:

    出于效率考虑,没有必要将所有成员函数都声明为虚函数。

    demo

    #include <iostream>
    using namespace std;
    
    //多态成立的三个条件 
    //要有继承  虚函数重写  父类指针指向子类对象 
    
    class Parent
    {
    public:
    	Parent(int a=0)
    	{
    		this->a = a;
    	}
    
    	virtual void print()  //1 动手脚  写virtal关键字 会特殊处理 //虚函数表
    	{
    		cout<<"父类"<<endl;
    	}
    	virtual void print2()  //1 动手脚  写virtal关键字 会特殊处理 //虚函数表
    	{
    		cout<<"父类"<<endl;
    	}
    private:
    	int a;
    };
    
    class Child : public Parent
    {
    public:
    	Child(int a = 0, int b=0):Parent(a)
    	{
    		this->b = b;
    	}
    
    	virtual void print()
    	{
    		cout<<"子类"<<endl;
    	}
    private:
    	int b;
    };
    
    void HowToPlay(Parent *base)
    {
    	base->print(); //有多态发生  //2 动手脚  
    	//效果:传来子类时,执行子类的print函数,传来父类时执行父类的print函数 
    	//C++编译器根本不需要区分是子类对象,还是父类对象
    	//父类对象和子类对象分步有vptr指针 , ==>虚函数表===>函数的入口地址
    	//迟绑定 (运行时的时候,c++编译器才去判断)
    }
    
    int main()
    {
    
    	Parent	p1; //3 动手脚 提前布局  
    				//用类定义对象的时候,C++编译器会在对象中添加一个vptr指针 
    	Child	c1; //子类里面也有一个vptr指针
    
    	HowToPlay(&p1);
    	HowToPlay(&c1);
    
    	return 0;
    }

    说明3 :C++编译器,执行HowToPrint函数,不需要区分是子类对象还是父类对象


    下面来证明vptr指针的存在。

    demo

    #include <iostream>
    using namespace std;
    
    
    class Parent1
    {
    public:
    	Parent1(int a=0)
    	{
    		this->a = a;
    	}
    
    	void print() 
    	{
    		cout<<"父类"<<endl;
    	}
    private:
    	int a;
    };
    
    class Parent2
    {
    public:
    	Parent2(int a=0)
    	{
    		this->a = a;
    	}
    
    	virtual void print()  
    	{
    		cout<<"虚析构函数的父类"<<endl;
    	}
    private:
    	int a;
    };
    
    int main()
    {
    	printf("sizeof(Parent):%d sizeof(Parent2):%d 
    ", sizeof(Parent1), sizeof(Parent2));
    	// 结果是普通类大小为4,而把函数变成虚构函数之后大小为8,所以证明了这里vptr指针的存在性
    
    	return 0;
    }
    



  • 相关阅读:
    关于quartusII 错误 Error: Current license file does not support the EP*** device 错误原因总结
    怎样用delphi关闭并重新启动 explorer.exe进程
    SQL创建链接服务器
    C#服务启动以及服务指令
    底部广告弹出
    选项卡
    tab切换
    pagex/y offsetx/y screenx/y clientx/y 用法及区别
    放大镜jQuery效果
    放大镜js实现效果
  • 原文地址:https://www.cnblogs.com/zhangyaoqi/p/4591558.html
Copyright © 2020-2023  润新知