精华内容
下载资源
问答
  • 多态实现原理

    2020-08-13 11:40:04
    在继承中构成多态有两个条件 1:必须通过基类的指针或者引用调用虚函数; 2:被调用的函数必须是虚函数,并且派生类必须对于虚函数进行重写;

    在继承中构成多态有两个条件
    1:必须通过基类的指针或者引用调用虚函数;
    2:被调用的函数必须是虚函数,并且派生类必须对于虚函数进行重写

    这就是我们的多态,但是多态是怎么实现的呢?

    
    class Base
    {
    public:
    	 void fun()
    	{
    		cout << "Base" << endl;
    	}
    private:
    	int m_a;
    	int m_b;
    };
    
    class A :public Base
    {
    public:
    	void fun()
    	{
    		cout << "A" << endl;
    	}
    private:
    	int a;
    	int b;
    };
    

    我们来看最基本的继承

        A a;
    	Base *p = &a;
    	p->fun();
    

    这是我们的赋值兼容规则,我们的子类可以给父类的指针或者是引用赋值,调用的父类的方法。
    但是如果我们的程序加上virtual

    
    class Base
    {
    public:
    	virtual void fun()
    	{
    		cout << "Base" << endl;
    	}
    private:
    	int m_a;
    	int m_b;
    };
    
    class A :public Base
    {
    public:
    	void fun()
    	{
    		cout << "A" << endl;
    	}
    	void print()
    	{
    		cout << "print" << endl;
    	}
    private:
    	int a;
    	int b;
    };
    int main()
    {
    	A a;
    	Base *p = &a;
    	p->fun();
    
    
    	system("pause");
    	return 0;
    }
    

    这时候就会调用我们的子类fun()。
    为什么会出现这种情况呢?

    
    class Base
    {
    public:
    	 void fun()
    	{
    		cout << "Base" << endl;
    	}
    private:
    	int m_a;
    	int m_b;
    };
    

    我们来看这个类的大小很明显是8,但是我们加了关键字virtual后

    class Base
    {
    public:
    	 virtual void fun()
    	{
    		cout << "Base" << endl;
    	}
    private:
    	int m_a;
    	int m_b;
    };
    

    这个类的大小是 12;
    我们来看这个类的成员
    在这里插入图片描述
    我们看到了这个__vfptr指针。

    virtual function pointer
    

    这个虚表函数指针始终只有一个,不管一个类里面有多少个虚函数
    这就是我们所说的虚函数表,这个__vfptr是一个指向虚函数表的指针。
    这个表里面所放的是虚函数的地址。虚表最后位置是nullptr表示结束

    这个是父类的虚函数表。
    子类在继承下来的时候会继承父类的虚表,但是子类进行了重写,就会更改虚函数表对应虚函数的地址,所以实现我们的赋值。

    但是我们怎么来用程序看到这个指针呢?
    __vfptr是隐藏在内部的,我们可以通过编译器查看,但是我们不能显式调用,我们拿到其首部的四个字节打印来看

    Base b;
    	printf("%x\n", *(int*)&b);
    	vfptr *vfptr_ar = (vfptr *)(*(int *)&b);
    	cout << vfptr_ar << endl;
    

    这就是虚函数的地址。
    打印虚表函数

    typedef void(*vfptr)();
    void Print(vfptr* ptr)
    {
    	for (int i = 0; ptr[i] != nullptr; ++i)
    	{
    		ptr[i]();
    	}
    }
    int main()
    {
    	/*
    	A a;
    	Base *p = &a;
    	p->fun();
    	a.fun();
    	//Base b;
    	//b = a;
    	//b.fun();
    	*/
    	Base b;
    	printf("%x\n", *(int*)&b);
    	vfptr *vfptr_ar = (vfptr *)(*(int *)&b);
    	cout << vfptr_ar << endl;
    	Print(vfptr_ar);
    	system("pause");
    	return 0;
    }
    
    展开全文
  • 主要介绍了Python面向对象多态实现原理及代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 探究C++多态实现原理

    2018-09-24 15:37:55
    探究C++多态实现原理 在之前的博客中讲到了如何使用C++多态的性质,实现面对对象的程序设计。 在这一篇文章中将会从多态的实现原理,_vfptr 以及多态类的内存构建模型上来探究C++多态的实现过程。 从以下代码来讨论...

    探究C++多态实现原理

    在之前的博客中讲到了如何使用C++多态的性质,实现面对对象的程序设计。

    在这一篇文章中将会从多态的实现原理,_vfptr 以及多态类的内存构建模型上来探究C++多态的实现过程。

    从以下代码来讨论:

    #include <iostream>
    using namespace std;
    
    typedef void(*VIRTUALL)();
    
    class Person
    {
    public:
    	virtual void fun1()
    	{
    		cout << endl << "this is Person fun 1" << endl;
    	}
    
    	virtual void fun2()
    	{
    		cout << endl << "this is Person fun 2" << endl;
    	}
    
    	virtual void fun3()
    	{
    		cout << endl << "this is Person fun 3" << endl;
    	}
    };
    
    class Boss :public Person
    {
    public:
    	virtual void fun1()
    	{
    		cout << endl << "this is Boss fun 1" << endl;
    	}
    
    	virtual void fun2()
    	{
    		cout << endl << "this is Boss fun 2" << endl;
    	}
    
    	virtual void fun3()
    	{
    		cout << endl << "this is Boss fun 3" << endl;
    	}
    };
    
    int main()
    {
    	Person p;
    	Boss b;
    	return 0;
    }
    

    在此段代码当中,我们为每个类定义了三个虚函数,并且对他们进行重写,如何监视这两个类的成员。

    运行起来打开监视
    在这里插入图片描述
    发现每个类都有一个 _vfptr (virtual function pointer)指针,指向的是一个数组的首地址,这个数组中存放着函数名,也就是函数地址。

    在C++中,一个指针类型的大小代表这个指针从首地址开始能往后面读到的字节数,比如 char* 类型的指针可以从首地址往后读一个字节大小, int * 类型的指针 能从首地址往后读四字节的大小,以此类推,我们可以将 类对象进行强制类型转换,因为类对象的前四个字节解引用指向的是描述的虚函数数组的地址空间,所以将其进行 int * 类型的地址转换,可以证明我们的猜测。
    代码如下:

    #include <iostream>
    using namespace std;
    
    typedef void(*VIRTUALL)();
    
    class Person
    {
    public:
    	virtual void fun1()
    	{
    		cout << endl << "this is Person fun 1" << endl;
    	}
    
    	virtual void fun2()
    	{
    		cout << endl << "this is Person fun 2" << endl;
    	}
    
    	virtual void fun3()
    	{
    		cout << endl << "this is Person fun 3" << endl;
    	}
    };
    
    class Boss :public Person
    {
    public:
    	virtual void fun1()
    	{
    		cout << endl << "this is Boss fun 1" << endl;
    	}
    
    	virtual void fun2()
    	{
    		cout << endl << "this is Boss fun 2" << endl;
    	}
    
    	virtual void fun3()
    	{
    		cout << endl << "this is Boss fun 3" << endl;
    	}
    };
    
    void PrintVirtual(int * _ptr)
    {
    	for (int i = 0; ; ++i) {
    		if (_ptr[i] == 0)
    			break;
    		else
    		{
    			VIRTUALL* fd = (VIRTUALL*)(_ptr+i);
    			(*fd)();
    		}
    	}
    }
    
    int main()
    {
    	Person p;
    	Boss b;
    	PrintVirtual((int*)(*((int*)&p)));
    	PrintVirtual((int*)(*((int*)&b)));
    	return 0;
    }
    

    实例化出来的类对象 p 和 b,我们先对其进行取地址操作,再强制类型转换为 int * 类型,这样他的前四个字节的就出来了,然后对地址进行解引用,可以得出虚表的首地址,再对其转换为 int *类型,可以实现每次四字节的访问。再对这每次的访问函数进行调用,就明白了其内存模型的构建方式。

    运行结果:
    在这里插入图片描述
    内存构建方式:
    在这里插入图片描述

    展开全文
  • 缓冲区问题和多态实现原理,总结别人的,感觉很经典,分享大家,釜底抽薪的解决了相关问题
  • 多态实现原理的探究

    目录

    多态实现原理的探究

    总体代码


    多态实现原理的探究

    多态成立的三个条件:要有继承;要有虚函数重写;要有父类指针指向子类对象。

    先建立一个父类:

    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++编译器才去判断)
    }

    创建主调函数进行:

    void main01()
    {
    
    	Parent	p1; //3 动手脚 提前布局  
    				//用类定义对象的时候 C++编译器会在对象中添加一个vptr指针 
    	Child	c1; //子类里面也有一个vptr指针
    
    	HowToPlay(&p1);
    	HowToPlay(&c1);
    
    	cout<<"hello..."<<endl;
    	system("pause");
    	return ;
    }

    那么以上是多态的代码实现,下面探究多态的实现原理:

    再以上的代码部分,编译器分别在注释中的"动手脚的三个地方"进行修改,实现多态。当所想多态的函数前面一旦加入了一个"virtual"关键字后,编译器编译的时候扫描到,就会偷偷的在对应的对象上添加一个"vptr"指针,把这个虚函数做成一个虚函数表(将存储多个虚函数的入口地址,比如类中有两个虚函数,这俩个虚函数的函数指针会放到一个表里面,),所以,编译器根本不需要区分是子类对象还是父类对象,就只需要区分,函数是虚函数或不是虚函数,如果是虚函数,父类对象有个"vptr"指针,子类对象也有一个,再根据这个"vptr"指针去找虚函数表(每个对象都有一个虚函数表,父类有一个,子类也有一个),再根据虚函数表再找函数的入口地址,这样就实现了迟绑定的效果(即只有在运行时,编译器才会去判断)。

    当然官方的实现原理描述如下:

    • 当类中声明虚函数时,编译器会在类中生成一个虚函数表
    • 虚函数表是一个存储类成员函数指针的数据结构
    • 虚函数表是由编译器自动生成与维护的
    • virtual成员函数会被编译器放入虚函数表中
    • 当存在虚函数时,每个对象中都有一个指向虚函数表的指针(C++编译器给父类对象、子类对象提前布局vptr指针;当进行howToPrint(Parent *base)函数是,C++编译器不需要区分子类对象或者父类对象,只需要再base指针中,找vptr指针即可。)
    • VPTR一般作为类对象的第一个成员

     整个过程:

    首先,根据编译器运行到对应的添加"virtual"关键字的位置,会把这个函数,加到虚函数列表中,注意的是,每个对象都会创建一个虚函数列表,存放这个对象中的虚函数。

     再接着,编译器运行都需要进行多态的代码时,会去判断所涉及到的函数是否为虚函数,不是的话,正常运行;如果是,会去原先定义的虚函数列表中去查找,进行调用。

    在虚函数列表中,根据p,去找去那个对象的虚函数列表中去找,如果在列表中查找到了,就会去进行调用:

     

    总体代码

    dm01_多态原理探究.cpp

    
    #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++编译器才去判断)
    }
    
    void main01()
    {
    
    	Parent	p1; //3 动手脚 提前布局  
    				//用类定义对象的时候 C++编译器会在对象中添加一个vptr指针 
    	Child	c1; //子类里面也有一个vptr指针
    
    	HowToPlay(&p1);
    	HowToPlay(&c1);
    
    	cout<<"hello..."<<endl;
    	system("pause");
    	return ;
    }

     

    展开全文
  • 在c++中,多态是通过一张虚函数表(virtual table)来实现的,简称v-table,在这个表中主要是一个类的虚函数地址,通过这张表来实现继承,覆写。 比如,一个父类有一个虚函数,这个虚函数地址放在v-table中,有一...

    转载请注明出处
    blog.yesiare.cn

    多态原理

    在c++中,多态是通过一张虚函数表(virtual table)来实现的,简称v-table,在这个表中主要是一个类的虚函数地址,通过这张表来实现继承,覆写。
    比如,一个父类有一个虚函数,这个虚函数地址放在v-table中,有一个子类继承了这个父类,并且覆写了这个虚函数,是如何复写的呢,由于虚函数表也继承了过来,就用新的虚函数地址将原来表中旧的替换掉,这样一来,通过父类指针指向子类的时候,就可以调用这个子类覆写过的虚函数,如果有不同子类都覆写了这个函数,那么只用父类指针就可以实现调用所有子类覆写过的函数,这就是c++的多态。

    虚函数表(v-table)存放在哪

    如果一个类包含虚函数,那么在这个类内存空间的开头,就存放着一个指向它虚函数表的指针,这个表类似一个指针数组,存放函数指针。
    比如有这样一个类。

    class A{
    public:
        virtual void func1(){cout<<"i am A::func1"<<endl;}
        virtual void func2(){cout<<"i am A::func2"<<endl;}
        virtual void func3(){cout<<"i am A::func3"<<endl;}
        
        int data;
    	int data2;
    };
    

    那么它在内存中的情况就是这样的
    c++1
    func1 是虚函数表中具体的函数指针 可以看着 * 型
    ptovtable 就是指向虚函数表的指针 可以把它看作 ** 型
    下面我们如何用代码来证明呢?

    #include<iostream>
    using namespace std;
    
    class A{
    public:
        virtual void func1(){cout<<"i am A::func1"<<endl;}
        virtual void func2(){cout<<"i am A::func2"<<endl;}
        virtual void func3(){cout<<"i am A::func3"<<endl;}
    
        int data;
        int data2;
    };
    int main(){
        //首先我们建立一个函数指针类型(无返无参,对应上面类中虚函数)
        typedef void (*Pf)(void);
        Pf f1,f2,f3;//一会用来放三个虚函数
        A a;//生成实例
    
        cout<<"对象的起始地址:"<<&a<<endl;  //out 0x69fef4
        //接下来要提取出 ptovtable,指针占4个字节(我的编译器是)
        //所以如下,先将a的地址转换成int* 这样解引用才能得到4个字节(因为int刚好四字节
        //然后将得到的地址转换成int**  看图,ptovtable 可看作**
        cout<<"vtable的起始地址: "<<(int **)*(int *)(&a)<<endl;//out 0x4850f8
        //现在我们已经得到vtable的起始地址了,下面 要提取第一个函数指针
        //将 ** 变成 *  解引用就行,第一个函数指针偏移是0
        cout<<"表中第一个位置存放的指针:"<<*((int **)*(int *)(&a)+0)<<endl;//out 0x42d790
        //我们得到了一个函数指针,但是别忘了,他现在还是int*型,而函数指针是 void(*)(void)型
        //so 再强转一下
        f1=(Pf)*((int **)*(int *)(&a)+0);
        f1();//out i am A::func1
        //其他的不用说了吧,表的结尾存放NULL
        cout<<"表中第二个位置存放的指针:"<<*((int **)*(int *)(&a)+1)<<endl;//输出自己看
        cout<<"表中第三个位置存放的指针:"<<*((int **)*(int *)(&a)+2)<<endl;//
        cout<<"表中第四个位置存放的指针:"<<*((int **)*(int *)(&a)+3)<<endl;//
        f2=(Pf)*((int **)*(int *)(&a)+1);
        f3=(Pf)*((int **)*(int *)(&a)+2);
        f2();
        f3();
        cout<<"by zsh"<<endl;
        return 0;
    }
    

    运行情况
    c2
    呼~ 打完收工
    注意平时用不到这个,知道这个东西就好
    有什么意义呢,加深理解把
    装完b就跑

    展开全文
  • (2)多态实现的三个条件  有继承、有virtual重写、有父类指针(引用)指向子类对象。 (3)多态的C++实现  virtual关键字,告诉编译器这个函数要支持多态;不要根据指针类型判断如何调用;而是要根据指针所指向的...
  • 继承与多态 --- 下 --- 重载与重写、多态实现原理、纯虚函数
  • java多态实现原理

    万次阅读 多人点赞 2016-07-22 16:46:24
    众所周知,多态是面向对象编程语言的重要特性,它允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。C++ 和 Java 作为当前最为流行的两种面向对象编程语言,其内部对于多态的支持到底是如何...
  • 深入理解c++多态实现原理

    万次阅读 2018-05-20 01:54:00
    多态是面向对象编程的核心思想之一,因此我们有必要深入探索一下它的实现原理。理解了原理才能更好的使用。前置条件 现有代码如下所示,非常简单的例子。通过基类的引用调用recv函数来触发多态。接下来的分析涉及...
  • 浅析多态实现原理

    2018-10-26 16:09:24
    实现过程 当我们在声明一个类时,编译器会自动帮我们创建一个虚函数表。 比如下面的这段代码: 编译器为我们生成的虚函数表 虚函数表: 虚函数表是由编译器自动产生的一种存储类成员函数的一种数据结构。其中虚...
  • 1. 多态分类 多态分为两类 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名 动态多态: 派生类和虚函数实现运行时多态...3. 多态实现的前提 赋值兼容规则是指在需要父类对象的地方可以使用子类对象
  • JAVA多态实现原理浅析

    2018-11-05 11:23:47
    Java 中使用多态特性的方法主要有,实现一个接口,实现抽象类的一个方法,覆盖父类的一个方法。   多态的底层实现是动态绑定,即在运行时才把方法调用与方法实现关联起来。动态绑定涉及很多 JVM 的细节,全部写...
  • Java多态实现原理

    2016-01-18 22:15:55
    Java引用类型转换方法表实现InvokeVirtual
  • 多态实现的三个条件  有继承、有virtual重写、有父类指针(引用)指向子类对象。 多态的C++实现  virtual关键字,告诉编译器这个函数要支持多态;不要根据指针类型判断如何调用;而是要根据指针所指向的实际...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 62,153
精华内容 24,861
关键字:

多态的实现原理