精华内容
下载资源
问答
  • 虚函数实现原理

    千次阅读 2019-03-10 18:05:42
    虚函数的实现原理:每个虚函数都会有一个与之对应的虚函数表,该虚函数表的实质是一个指针数组,存放的是每一个对象的虚函数入口地址。对于一个派生类来说,他会继承基类的虚函数表同时增加自己的虚函数入口地址,...

    虚函数的实现原理:

    虚函数的用法:可以让派生类重写基类的成员函数实现多态。虚函数实现多态的机制,严格来说是动态多态,是在出现运行的时候实现的。

    虚函数的实现原理:每个虚函数都会有一个与之对应的虚函数表,该虚函数表的实质是一个指针数组,存放的是每一个对象的虚函数入口地址。对于一个派生类来说,他会继承基类的虚函数表同时增加自己的虚函数入口地址,如果派生类重写了基类的虚函数的话,那么继承过来的虚函数入口地址将被派生类的重写虚函数入口地址替代。那么在程序运行时会发生动态绑定,将父类指针绑定到实例化的对象实现多态。

     更多详见:https://blog.csdn.net/xiejingfa/article/details/50454819

    展开全文
  • 1.带有虚函数的对象模型 我们先看段代码: #include<iostream> using namespace std; class B1 { public: void ...

    1.带有虚函数的基类对象模型

    我们先看段代码:

    #include<iostream>
    using namespace std;
    class B1
    {
    	public:
    		void func1()
    		{}
    		int _b;
    };
    class B2
    {
    	public:
    		virtual void func()
    		{}
    		int _b;
    };
    int main()
    {
    	cout<<"sizeof(B1) = "<<sizeof(B1)<<endl;  
    	cout<<"sizeof(B2) = "<<sizeof(B2)<<endl;
    	system("pause");
    	return 0;
    }
    

    运行结果:
    在这里插入图片描述
    可以看出,B2的这个类比B1多了4个字节,而这4个字节就是用来存放虚函数的地址,也就是说,这4个字节的数据是一个指针(地址),这个指针指向的是虚函数地址。看个图就很容易理解。
    在这里插入图片描述
    我们需要注意的是:
    1.B2对象的前4个字节存放的是虚表的地址,其后才是B2该对象的成员变量;(虚函数表我们也叫做虚表)。
    2.若B2这个类中有多个虚函数,那么其对象大小还是8,因为前4个字节是存放虚函数表的地址,在这个虚函数表(函数指针数组)里面每个元素才是每个虚函数的地址。

    2.派生类对象虚函数表如何构建?

    上个例子,只是给出了带有虚函数基类的对象模型,那派生类的对象虚函数表应该如何构建?

    #include<iostream>
    #include<string>
    using namespace std;
    class Base					//基类
    {
    public:
    	virtual void TestFunc1()
    	{
    		cout << "Base::TestFunc1()" << endl;
    	}
    	virtual void TestFunc2()
    	{
    		cout << "Base::TestFunc2()" << endl;
    	}
    	virtual void TestFunc3()
    	{
    		cout << "Base::TestFunc3()" << endl;
    	}
    	int _b;
    };
    class Derived : public Base					//派生类
    {
    public:
    	virtual void TestFunc4()
    	{
    		cout << "Derived::TestFunc4()" << endl;
    	}
    	virtual void TestFunc1()
    	{
    		cout << "Derived::TestFunc1()" << endl;
    	}
    
    	virtual void TestFunc3()
    	{
    		cout << "Derived::TestFunc3()" << endl;
    	}
    	virtual void TestFunc5()
    	{
    		cout << "Derived::TestFunc5()" << endl;
    	}
    	int _d;
    };
    typedef void(*PVFT)();				//声明函数指针,用来调用虚函数
    
    void PrintVFT(Base& b, const string& str)
    {
    	cout << str << endl;
    	/*这里是先将对象的地址取出来再强制类型换,此时再解引用的话,取的值就是对象前四个字节地址里面存放的值,
    	这个值就是虚表的地址,即就是函数指针数组的首地址,我们再将这个地址转换成函数指针类型*/
    	PVFT* pVFT = (PVFT*)(*(int*)&b)while (*pVFT)					//虚表中最后一个元素是空值,打印完循环退出
    	{
    		(*pVFT)();			// 再解引用就是函数指针数组里面的第一个元素(即就是第一个虚函数地址),再往后一次打印
    		++pVFT;
    	}
    	cout << endl;
    }
    void TestVirtualFunc(Base& b)
    {
    	b.TestFunc1();
    	b.TestFunc3();
    	return;
    }
    int main()
    {
    	Base b;
    	Derived d;
    	// 打印基类与派生类的虚表
    	PrintVFT(b, "Base VFT:");
    	PrintVFT(d, "Derived VFT:");
    	// 传递Base类对象
    	TestVirtualFunc(b);
    	cout << endl;
    	// 传递派生类对象
    	TestVirtualFunc(d);
    	system("pause");
    	return 0;
    }
    

    运行结果:
    在这里插入图片描述
    根据结果,我们首先可以看出基类虚函数表是按照声明顺序依此存放,派生类的虚函数表则相应的发生了一些改变。
    1.先将基类中的虚表内容拷贝一份到派生类虚表中
    2.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数替换虚表中基类的虚函数
    3.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后

    其实我们没必要刻意去记这些规则,如果用多态的思想去考虑下的话,这样的规则很合理。对我自己而言,首先派生类继承基类,那么基类有的东西,派生类本来没有的东西继承之后也就具有;其次,若基类和派生类都具有相同的东西,那么在派生类的虚表中,我派生类要保持我自己的特点,所以此时派生类的虚表中存放的是自己的虚函数,这样做的目的很简单,就是为了在多态调用时,会很灵活,根据对象本身自己来调用相应的虚函数,假如派生类中的虚表中存放的是基类的虚函数,那么请问,在多态调用时,不管给基类还是派生类的对象,调用的虚函数都是级基类的虚函数,这样做还会实现多态这个特性吗?最后一点不言而喻,派生类独有的虚函数按照声明顺序加在虚表后面即可。
    讨论完基类和派生类对象的虚表之后,我们来看看派生类对象完整的对象模型。

    3.单继承的派生类对象模型

    对于单继承的派生类的对象模型,其和我们上面介绍的第一个例子一样,前四个字节是虚表的地址,后面就是按顺序存放
    接着看一段代码:

    #include<iostream>
    using namespace std;
    
    class Base
    {
    public:
    	virtual void func1()
    	{
    		cout << "Base::func1" << endl;
    	}
    
    	virtual void func2()
    	{
    		cout << "Base::func2" << endl;
    	}
    private:
    	int a;
    };
    
    class Derive :public Base
    {
    public:
    	virtual void func1()
    	{
    		cout << "Derive::func1" << endl;
    	}
    
    	virtual void func3()
    	{
    		cout << "Derive::func3" << endl;
    	}
    
    	virtual void func4()
    	{
    		cout << "Derive::func4" << endl;
    	}
    private:
    	int b;
    };
    
    typedef void(*VFUNC)();
    
    void PrintVTbale(int* table)
    {
    	printf("vtable:%p\n", table);
    	int i = 0;
    	
    	for (int i = 0; table[i]!= NULL; ++i) //虚表最后一个元素为空值
    	{	
    		printf("table[%d]:%p->", i, table[i]);
    		VFUNC f = (VFUNC)table[i];	//转为函数指针
    		f();	//调用对应的虚函数
    	}
    	return;
    }
    

    运行结果:
    在这里插入图片描述
    根据结果:基类的对象模型和上面第一个例子一样,不再过多讨论。对于单继承的派生类对象模型,在我们清楚了其虚表的构建之后,再其虚函数表地址后面按顺序先存放基类的对象,其次再存放派生类的对象即可。
    派生类的对象模型那就是在虚函数指针后面,按顺序先存放基类的成员变量,接着再存放派生类自己成员变量。如下图:
    在这里插入图片描述
    下面我们来看多继承的派生类的对象模型

    4.多继承的派生类对象模型

    看代码:

    #include<iostream>
    using namespace std;
    
    class Base1				//基类Base1
    {
    public:
    	virtual void func1()
    	{
    		cout << "Base1::func1" << endl;
    	}
    	virtual void func2()
    	{
    		cout << "Base1::func2" << endl;
    	}
    private:
    	int b1;
    };
    
    class Base2				//基类Base2
    {	
    public:
    	virtual void func1()
    	{
    		cout << "Base2::func1" << endl;
    	}
    	virtual void func2()
    	{
    		cout << "Base2::func2" << endl;
    	}
    private:
    	int b2;
    };
    
    class Derive : public Base1, public Base2		//派生类
    {
    public:
    	virtual void func1() {
    		cout << "Derive::func1" << endl;
    	}
    	virtual void func3()
    	{
    		cout << "Derive::func3" << endl;
    	}
    private:
    	int d1;
    };
    
    typedef void(*VFUNC)();
    
    void PrintVTbale(int* table)
    {
    	printf("vtable:%p\n", table);
    	for (int i = 0; table[i] != 0; ++i)
    	{
    		printf("table[%d]:%p->", i, table[i]);
    		VFUNC f = (VFUNC)table[i];
    		f();
    	}
    	cout << endl;
    }
    
    int main()
    {
    	Base1 b1;
    	Base2 b2;
    
    	Derive d;
    	cout << sizeof(d) << endl;		//计算派生类对象的大小
    
    	PrintVTbale((int*)(*(int*)&b1));
    	PrintVTbale((int*)(*(int*)&b2));
    
    	PrintVTbale((int*)(*(int*)&d));		//第一个虚表
    	PrintVTbale((int*)(*(int*)((char*)&d + sizeof(Base1))));	//第二个虚表
    	system("pause");
    	return 0;
    }
    

    运行结果:

    在这里插入图片描述
    我们注意到;派生类对象的大小是20,对于多继承的派生类对象,如果只有一个虚表,那么它的大小应该是4(虚表指针) + 4(b1)+ 4(b2) + 4(d1) = 16,可是结果不是16,所以派生类对象的虚表应该不止一个。
    所以他的对象模型应该如下图这样:
    在这里插入图片描述
    总结以下就是:
    1.对于多继承的派生类的对象,其不但继承了基类的对象,也继承了基类的虚函数表指针;
    2.派生类继承多个基类,派生类的对象模型其实就相当于将基类的对象模型继承下来了,只不过对于派生类独有的虚函数,那么他的虚函数指针将会按顺序存放在第一个虚表中最后的位置。
    3.最后再加上派生类自己成员

    至此,对于常见的普通类型多态调用就这些,还有其他继承类型的多态调用,如菱形继承等。
    后面会再做总结。

    注:文中如有不正之处,欢迎指出,希望和大家共同学习,进步。
    
    展开全文
  • 多态的实现效果:多态实现了同样的调用语句具有不同的表现形态。 多态成立的三个条件:继承、徐函数重写、父类指针指向子类对象。 多态的C++实现方法:virtual关键字,告诉编译器要执行多态;不要根据指针类型来...

    1-多态

    1. 多态的实现效果:多态实现了同样的调用语句具有不同的表现形态。
    2. 多态成立的三个条件:继承、徐函数重写、父类指针指向子类对象。
    3. 多态的C++实现方法:virtual关键字,告诉编译器要执行多态;不要根据指针类型来判断如何调用对应的函数,而是要根据指针所实际指向的对象的类型来判断如何调用。
    4. 多态的理论基础:动态联编(if、switch、多态)、静态联编、根据指针实际指向的对象的类型来判断重写函数的调用。
    5. 多态的重要意义:多态是设计模式的基础。
    6. 实现多态的理论基础:函数指针做函数参数。
    7. C++中多态的实现原理:1.当在类中声明虚函数时,编译器会在类中生成一个虚函数表;2.虚函数表是一个存储类成员函数指针的数据结构;3.虚函数表由编译器自动生成与维护;4.带有virtual关键字的虚函数会被编译器放入到虚函数表中;5.当类族中存在虚函数时,类族定义的每一个对象都有一个指向虚函数表的指针(VPTR指针)。
    8. 利用VPTR指针,C++编译器根据父类指针实际指向的对象的类型,转到该对象的虚函数表中,去查找相应的虚函数进行执行,从而实现了多态。

    2-虚函数

    1. 问题1:C++编译器是如何对虚函数进行特殊处理的?
    2. 问题2:指针做函数参数。使用父类形参指针,接受实参,编译器如何知道接收到的是父类对象还是子类对象的指针?
    3. 问题3:多态是C++中的类指针特有的现象,且只有符合多态成立的三个条件时才会出现。类的对象不存在多态。如图:
    4. 虚函数表:
    5. C++编译器在程序编译期间,在函数的指针形参中加了一个元素?这个元素是对外隐藏的,但是可以表达出该对象是子类对象还是父类对象,就能够达到调用相应类型对象的虚函数的目的。
    6. 只要在类中有加了virtual关键字的虚函数,c++编译器就会为类生成一个VPTR指针,是隐藏到类的对象中的。

      VPTR指针指向虚函数表,在调用虚函数时,不同对象的VPTR指针,会根据他实际指向的对象的类型,查找指向对象的虚函数表,调用相应的虚函数。

    7. 虚函数的执行过程:对象类型->VPTR指针->虚函数表->虚函数执行。

    8. VPTR指针存在的证明:对于只含有成员函数的类,成员函数在内存四区中的函数区,所以类的大小只占用1个字节,是占位符占用的大小。但是加virtual后,类占用的大小变为了4个字节,说明此时有了VPTR指针的存在。
    9. 如果是继承的话,会为子类单独生成一个新的VPTR指针,不会继承父类的VPTR指针。编译器会特殊处理。

    10. 编译器会:1.创建虚函数表;2.为类的对象自动添加vptr指针。

    11. 实际上,由于涉及到使用VPTR指针进行虚函数表中的虚函数的寻址操作,所以虚函数的执行效率比一般的成员函数要低很多,所有没有必要把所有的成员函数都定义成虚函数,而是根据实际需求来进行函数定义,然后在子类中根据实际的业务需求进行虚函数的重写。

    12.  

    展开全文
  • 虚函数表工作原理

    2012-11-02 13:22:11
    对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。 在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应...
  • C++虚函数实现原理

    2015-10-24 21:11:09
    虚函数表中虚函数的分布情况;其中包括发生继承的情况下虚函数表中虚函数的分布情况;
  • C++虚函数和虚函数表原理

    万次阅读 多人点赞 2018-07-26 19:49:54
    运行期多态就是通过虚函数和虚函数表实现的。 类的对象内部会有指向类内部的虚表地址的指针。通过这个指针调用虚函数。 虚函数的调用会被编译器转换为对虚函数表的访问: ptr-&amp;gt;f(); //ptr代表this...

    虚函数的地址存放于虚函数表之中。运行期多态就是通过虚函数和虚函数表实现的。

    类的对象内部会有指向类内部的虚表地址的指针。通过这个指针调用虚函数。

    虚函数的调用会被编译器转换为对虚函数表的访问:

    ptr->f();   //ptr代表this指针,f是虚函数
    *(ptr->vptr[1])(ptr);

    上述代码中,ptr代表一个this指针,ptr指向的vptr是类内部的虚表指针。这个虚表指针会被放在类的最前方(VS2017),1就是虚函数指针在虚函数表中的索引值。在这个索引值表示的虚表的槽中存放的就是f()的地址。

    虚表指针的名字也会被编译器更改,所以在多继承的情况下,类的内部可能存在多个虚表指针。通过不同的名字被编译器标识。

    虚函数表中可能还存在其他的内容,如用于RTTI的type_info类型。或者直接将虚基类的指针存放在虚表中。

    压制多态可以通过域操作符进行。

    class A1
    {
    public:
        virtual void f() { cout << "A1::f" << endl; }
    };
    class C : public A1
    {
    public:
        virtual void f() { cout << "C::f" << endl; }
    };
    c.A1::f();  //A1::f
    c.f();  //C::f

    单继承

    这种情况下,派生类中仅有一个虚函数表。这个虚函数表和基类的虚函数表不是一个表(无论派生类有没有重写基类的虚函数),但是如果派生类没有重写基类的虚函数的话,基类和派生类的虚函数表指向的函数地址都是相同的。

    class A1
    {
    public:
        A1(int _a1 = 1) : a1(_a1) { }
        virtual void f() { cout << "A1::f" << endl; }
        virtual void g() { cout << "A1::g" << endl; }
        virtual void h() { cout << "A1::h" << endl; }
        ~A1() {}
    private:
        int a1;
    };
    class C : public A1
    {
    public:
        C(int _a1 = 1, int _c = 4) :A1(_a1), c(_c) { }
        //virtual void f() { cout << "C::f" << endl; }
        //virtual void g() { cout << "C::g" << endl; }
        //virtual void h() { cout << "C::h" << endl; }
    private:
        int c;
    };

    类C没有重写A的虚函数,所以虚函数表内部的情况如下:

    这里写图片描述

    可以看出,两个类的__vfptr的值不同,但是每个槽内部的函数地址都是相同的。

    这里写图片描述

    如果类C中重写了A类中的函数:

    class C : public A1
    {
    public:
        C(int _a1 = 1, int _c = 4) :A1(_a1), c(_c) { }
        virtual void f() { cout << "C::f" << endl; }
        virtual void g() { cout << "C::g" << endl; }
        virtual void h() { cout << "C::h" << endl; }
    private:
        int c;
    };

    这里写图片描述

    那么就会覆盖A类的虚函数,重写一部分就会覆盖一部分,重写全部就会覆盖全部。

    这里写图片描述

    如果C中重新写了一些别的虚函数,那么这些虚函数将排在父类的后面,这里编译器无法显示,可以通过打印虚表来进行。

    打印的过程比较简单,通过访问类C的前8字节(64位编译器)找到虚函数表,再一次遍历虚函数表即可。虚函数表最后一项用的是0,代表虚函数表结束。

    C c;
    long long *p = (long long *)(*(long long*)&c);
    typedef void(*FUNC)();        //重定义函数指针,指向函数的指针
    void PrintVTable(long long* vTable)  //打印虚函数表
    {
        if (vTable == NULL)
        {
            return;
        }
        cout << "vtbl:" << vTable << endl;
        int  i = 0;
        for (; vTable[i] != 0; ++i)
        {
            printf("function : %d :0X%x->", i, vTable[i]);
            FUNC f = (FUNC)vTable[i];
            f();         //访问虚函数
        }
        cout << endl;
    }

    通过这样的打印可以得知C的虚函数表为:

    vtbl:00007FF6CD2CBE68
    function : 0 :0Xcd2c115e->A1::f
    function : 1 :0Xcd2c146a->A1::g
    function : 2 :0Xcd2c1113->A1::h

    vtbl:00007FF6CD2CBEA8
    function : 0 :0Xcd2c115e->A1::f
    function : 1 :0Xcd2c146a->A1::g
    function : 2 :0Xcd2c1113->A1::h
    function : 3 :0Xcd2c1023->C::f
    function : 4 :0Xcd2c132a->C::g
    function : 5 :0Xcd2c11d1->C::h

    具体的图解为:

    这里写图片描述

    多继承

    多继承情况下,派生类中有多个虚函数表,虚函数的排列方式和继承的顺序一致。派生类重写函数将会覆盖所有虚函数表的同名内容,派生类自定义新的虚函数将会在第一个类的虚函数表的后面进行扩充

    class A1
    {
    public:
        A1(int _a1 = 1) : a1(_a1) { }
        virtual void f() { cout << "A1::f" << endl; }
        virtual void g() { cout << "A1::g" << endl; }
        virtual void h() { cout << "A1::h" << endl; }
        ~A1() {}
    private:
        int a1;
    };
    class A2
    {
    public:
        A2(int _a2 = 2) : a2(_a2) { }
        virtual void f() { cout << "A2::f" << endl; }
        virtual void g() { cout << "A2::g" << endl; }
        virtual void h() { cout << "A2::h" << endl; }
        ~A2() {}
    private:
        int a2;
    };
    class A3
    {
    public:
        A3(int _a3 = 3) : a3(_a3) { }
        virtual void f() { cout << "A3::f" << endl; }
        virtual void g() { cout << "A3::g" << endl; }
        virtual void h() { cout << "A3::h" << endl; }
        ~A3() {}
    private:
        int a3;
    };
    
    class B : public A1, public A2, public A3
    {
    public:
        B(int _a1 = 1, int _a2 = 2, int _a3 = 3, int _b = 4) :A1(_a1), A2(_a2), A3(_a3), b(_b) { }
        virtual void f1(){ cout << "B::f" << endl; }
        virtual void g1(){ cout << "B::g" << endl; }
        virtual void h1(){ cout << "B::h" << endl; }
    private:
        int b;
    };

    这里通过编译器的部分可以看出来,未被重写的虚函数指针将和基类指向同一个位置,一旦被重写,函数指针就指向新的位置。

    这里写图片描述

    在B类中,函数指针指向的位置不变:

    这里写图片描述

    而这时候B类中第一个虚函数表已经增加了新的项,从打印结果可知。

    vtbl:00007FF7DD62BF38
    function : 0 :0Xdd621177->A1::f
    function : 1 :0Xdd621497->A1::g
    function : 2 :0Xdd621127->A1::h

    vtbl:00007FF7DD62BF78
    function : 0 :0Xdd6212df->A2::f
    function : 1 :0Xdd62105f->A2::g
    function : 2 :0Xdd6213fc->A2::h

    vtbl:00007FF7DD62BFB8
    function : 0 :0Xdd621032->A3::f
    function : 1 :0Xdd62129e->A3::g
    function : 2 :0Xdd621221->A3::h

    vtbl:00007FF7DD62BFF8
    function : 0 :0Xdd621177->A1::f
    function : 1 :0Xdd621497->A1::g
    function : 2 :0Xdd621127->A1::h
    function : 3 :0Xdd62144c->B::f
    function : 4 :0Xdd621019->B::g
    function : 5 :0Xdd62133e->B::h

    而如果B类重写了函数,那么打印结果将是:

    vtbl:00007FF720C8BF38
    function : 0 :0X20c8117c->A1::f
    function : 1 :0X20c814b5->A1::g
    function : 2 :0X20c8112c->A1::h

    vtbl:00007FF720C8BF78
    function : 0 :0X20c812f8->A2::f
    function : 1 :0X20c8105a->A2::g
    function : 2 :0X20c8141a->A2::h

    vtbl:00007FF720C8BFB8
    function : 0 :0X20c8102d->A3::f
    function : 1 :0X20c812b2->A3::g
    function : 2 :0X20c81230->A3::h

    vtbl:00007FF720C8BFF8
    function : 0 :0X20c814ab->B::f
    function : 1 :0X20c81370->B::g
    function : 2 :0X20c81393->B::h

    并且此时B类的信息为:

    这里写图片描述

    从编译器给出的信息我们可以看到在第二个虚函数表中有adjustor{16}的字样,这就是A类的大小,也就是说,这就是告诉编译器,需要进行16字节的偏移(thunk技术)。这就引出了接下来的一个问题:

    B类用不同的基类指针指向的时候,运行的是不同的基类中的虚函数(这就是多态的表现),这里可以知道,当A2类指针指向B的时候,虚函数指针是自动跳到B类中A2类所在的地方的,这个跳转是怎么进行的呢?

    首先在编译期,就可以知道一个指针需要偏移多少个字节:

    A2 *p = new B;

    编译器会将这个代码改为:

    B *tmp = new B;
    A2 *p = tmp ? tmp + sizoef(A1) : 0;

    经过这样的调整A1,A2,A3都会指向正确的类的位置。但是这样还不够。

    由上面的编译器信息图我们可以知道,当B类重写了函数之后,A2,A3的虚函数表所指对象已经不再是简单的函数指针了,而是一个thunk对象。这就是C++的thunk技术。

    所谓的thunk就是一段汇编代码,这段汇编代码可以以适当的偏移值来调整this指针以跳到对应的虚函数中去,并调用这个函数,也就是说当使用A1的指针指向B的对象,不需要发生偏移,而使用A2的指针指向B则需要进行偏移sizeof(A1)个字节。并跳转到A1中的函数来执行。这就是通过thunk的jmp指令跳转到这个函数。

    所以具体的虚函数表中的情况如下:

    1. 如果两个基类中的虚函数名字不同,派生类只重写了第二个基类的虚函数,则不会产生thunk用以跳转。
    2. 如果基类中虚函数名字相同,派生类如果重写,将会一次性重写两个基类的虚函数,这时候第二个基类的虚函数表中存放的就是thunk对象,当指针指向此处的时候,会自动跳转到A类的对应虚函数(已经被B重写)执行。
    3. 第一个基类的虚函数被重写与否都不会产生thunk对象,因为这个类是被别的基类指针跳转的目标,而这个类的指针施行多态的时候是不会发生跳转的。
    4. 派生类的重新定义的虚函数将会排在第一个虚函数表内部A1虚函数的后面,但是当A2调用这个函数的时候,会通过thunk技术跳转回第一个类的虚函数表以执行相对应的虚函数。
    5. 除了第一个基类的虚析构函数,其他基类的析构函数都是thunk对象。

    综上所述,thunk对象用于所有基类都被派生类重写后,调用虚函数将跳到最开始的基类部分。或者派生类中定义的虚函数也会跳转到第一个基类的虚函数表中。而仅出现在后面的基类的虚函数表中的虚函数,无论被重写与否都不会产生thunk对象。因为这里不会在第一个基类中由对应的虚函数指针。

    展开全文
  • C/C++虚函数和虚函数表概念及实现原理1. 概述2. 虚函数表构造过程3. 虚函数调用过程4. 多重继承 1. 概述 每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类...
  • C++面试题之虚函数()实现机制

    千次阅读 多人点赞 2018-04-27 20:01:25
    虚函数实现多态(动态绑定)/接口函数的基础. 可以说: 没有虚函数, C++将变得一无是处!既然是C++的精髓, 那么我们有必要了解一下她的实现方式吗? 有必要! 既然C++是从C语言的基础上发展而来...
  • c++虚函数实现原理

    2020-04-21 22:20:57
    c++虚函数是通过虚函数表实现的,当一个类里含有虚函数时,编译器会给这个类分配一个虚函数表,虚函数表里存放的是虚函数的地址。当该类被继承时,其子类也会继承基类的虚函数表。 比如基类含有一个虚函数 f(), ...
  • c++虚函数实现原理

    千次阅读 2018-08-04 09:09:24
    C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。...
  • 虚函数实现多态原理

    千次阅读 2019-04-09 14:23:23
    虚函数实现多态原理学习。
  • 虚函数:多态的实现原理

    千次阅读 2019-09-07 18:00:07
    C++ 中的虚函数的作用主要是实现了多态的机制。关于多态,说白了就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数(当然引用也可以达到该目的,引用也是指针的另一种变种)。这种技...
  • C++虚函数表原理

    2020-02-26 00:13:38
    本文介绍虚函数表是如何实现动态绑定的。 二、类的虚表 每个包含了虚函数的类都包含一个虚表。 我们知道,当一个类(A)继承另一个类(B)时,类A会继承类B的函数的调用权。所以如果一个基类包含了虚函数,那么其...
  • 虚函数实现原理

    万次阅读 多人点赞 2018-09-02 10:28:21
    C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。...
  • 简单地说,每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针。例: 其中: B的虚函数表中存放着B::foo和B::bar两个函数指针。 D...
  • C++中 的虚函数的作用主要是实现了多态的机制。...每个含有虚函数的类都有一个虚函数表(Virtual Table)来实现的。简称为V-Table。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证...
  • C++虚函数的底层实现原理

    千次阅读 2018-05-27 15:49:36
    原文链接:https://blog.csdn.net/u011000290/article/details/50498683在C++中,多态是利用虚函数实现的。比如说,有如下代码:[cpp] view plain copy#include &lt;iostream&gt; using namespace ...
  • c++中虚函数表的作用主要是实现了多态的机制。首先先解释一下多态的概念,多态是c++的特点之一,关于多态,简而言之就是 用父类的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数,这种方法呢,...
  • C++常见面试题:虚函数实现原理

    千次阅读 2017-08-15 17:04:52
    转载地址:http://blog.csdn.NET/wanghaobo920/article/details/7674631前言C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类...
  • c++ 虚函数实现机制

    2017-03-07 17:39:48
    虚函数在c++中的实现机制就是用虚和虚指针,但是具体是怎样的呢?从more effecive c++其中一篇文章里面可以知道:是每个类用了一个虚,每个类的对象用了一个虚指针。具体的用法如下: class A { public: ...
  • 编译器到底做了什么实现虚函数的晚绑定呢?我们来探个究竟。  编译器对每个包含虚函数的类创建一个(称为V TA B L E)。在V TA B L E中,编译器放置特定类的虚函数地址。在每个带有虚函数的类 中,编译器秘密地...
  • 虚函数表工作原理 C++中的虚函数的作用主要是实现了多态的机制关于多态简而言之就是用父类型别的指针指向其子类的实例然后通过父类的指针调用实际子类的成员函数这种技术可以让父类的指针有多种形态这是一种泛型技术...
  • C++ 虚函数实现虚函数表 虚表指针

    千次阅读 2015-09-24 17:58:56
    virtual(虚函数)的实现
  • C++虚函数实现原理

    万次阅读 2017-11-08 22:49:34
    C++中的虚函数主要是用来实现多态(面向对象的三大特性之一)的。 下面是一个实现多态的错误例子: // 基类 // class Base { public: Base() { printf("Call Base::Base()\n"); } ~Base() { } ...
  • 简单地说,每一个含有虚函数(无论是其本身的,还是继承而来的)的类都至少有一个与之对应的虚函数表,其中存放着该类所有的虚函数对应的函数指针。例: 其中: B的虚函数表中存放着B::foo和B::bar两个函数指针。 ...
  • 接口方法表和虚方法表的原理和C++的虚函数表类似。 C++的虚函数和纯函数的区别 纯函数: 在基类中只进行声明,不进行具体实现,而在子类进行具体实现,类似于java接口中的方法。 注意: C++允许多重继承,是通过抽象...
  • 1、对于C++中的虚函数一直都是靠着死记硬背的方式在使用,今天特地查阅了一下它的实现原理。 2、虚函数:C++中为了实现多样性的一个工具。使用virtual关键字修饰的函数即为虚函数,派生类自动继承虚函数性质。使用...
  • 静态绑定与动态绑定 讨论静态绑定与动态绑定,首先需要理解的是绑定,何为绑定?函数调用与函数本身的关联,以及成员访问与变量内存地址间的关系,称为...而虚函数是通过虚函数表实现的。这个表中记录了虚函数的地址,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,359
精华内容 14,943
关键字:

虚函数表的实现原理