29虚函数-静态绑定-动态绑定

虚函数-静态绑定-动态绑定

  • 如果类中定义了虚函数,那么编译阶段,编译器会给这个类类型产生一个唯一的vftable虚函数表,其中主要存储的是RTTI指针和虚函数的地址。程序运行时,每一张虚函数表都会加载到内存的.rodata只读数据区。
  • 一个类中定义了虚函数,那么这个类的对象,其运行时,内存中开始部分,会多存储第一个vfptr虚函数指针,指向相应类型的虚函数表vftable。一个类型定义的n个对象,他们的vfptr指向的都是同一个虚函数表。
  • 一个类里面虚函数的个数,不影响对象的内存大小(vfptr),只影响虚函数表的大小。
  • 如果派生类中的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,而且基类的方法是virtual虚函数,那么派生类的这个方法,自动处理成虚函数。重写<=>覆盖。因此,覆盖指的是虚函数表中虚函数地址被新的重写的虚函数覆盖。
  • 总而言之,虚函数表指针存在的意义:一个父类指针指向的究竟是什么类的对象?这个动态信息被存储在虚表指针

virtual

class A {
public:
    virtual void vfunc1();
    virtual void vfunc2();
    void func1();
    void func2();
private:
    int m_data1, m_data2;
};
class B : public A {
public:
    void vfunc1();  //自动处理成虚函数。
    void func1();
private:
    int m_data3;
};
class C: public B {
public:
    void vfunc2();  //自动处理成虚函数。
    void func2();
private:
    int m_data1, m_data4;
};

virtual_

#include<iostream>
#include<typeinfo>
using namespace std;

#if 0
class Base
{
public:
	Base(int data = 10) : ma(data) {}
	void show() { cout << "Base::show()" << endl; }
	void show(int) { cout << "Base:show(int)" << endl; }
protected:
	int ma;
};

class Derive : public Base
{
public:
	Derive(int data = 20) : Base(data), mb(data) {}
	void show() { cout << "Derive::show()" << endl; }
private:
	int mb;
};

int main()
{
	Derive d(50);
	Base* pb = &d;
	pb->show(); // 静态(编译时期)的绑定(函数的调用)call        Base::show (0851037h)
	pb->show(10); // 静态绑定 call        Base::show (0851307h)

	cout << sizeof(Base) << endl; // 4
	cout << sizeof(Derive) << endl; // 8

	cout << typeid(pb).name() << endl; // Base*
	cout << typeid(*pb).name() << endl; // Base

	return 0;
}
#endif

# if 1
class Base
{
public:
	Base(int data = 10) : ma(data) {}
	virtual void show() { cout << "Base::show()" << endl; }
	virtual void show(int) { cout << "Base:show(int)" << endl; }
protected:
	int ma;
};

class Derive : public Base
{
public:
	Derive(int data = 20) : Base(data), mb(data) {}
	/*
	如果派生类的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,
	而且基类的方法是virtual虚函数,那么派生类的这个方法,自动处理成虚函数
	重写《=》覆盖
	*/
	void show() { cout << "Derive::show()" << endl; }
private:
	int mb;
};

int main()
{
	Derive d(50);
	Base* pb = &d;

	/*
	pb -> Base Base::show 如果发现show是普通函数,就进行静态绑定
	pb -> Base Base::show 如果发现show是虚函数,就进行动态绑定
	mov eax, dword ptr[pb]  // 取pb的vfptr放入eax
	mov ecx, dword ptr[eax] // 将vfptr指向的虚函数地址放入ecx
	call ecx // 动态(运行时期)的绑定(函数的调用)
	*/

	pb->show(); // Derive::show()
	pb->show(10); // Base:show(int)

	cout << sizeof(Base) << endl; //
	cout << sizeof(Derive) << endl; // 

	/*
	pb的类型:Base -> 有没有虚函数
	如果Base没有虚函数,*pb识别的就是编译时期的类型  *pb  <=> Base类型
	如果Base有虚函数,*pb识别的就是运行时期的类型 RTTI类型
	pb->d(vfptr)->Derive
	*/
	cout << typeid(pb).name() << endl; // class Base *
	cout << typeid(*pb).name() << endl; // class Derive

	return 0;
}
#endif

虚函数的权限问题

class Base
{
public:
	Base()
	{
		cout << "call Base()" << endl;
	}
	virtual void show()
	{
		cout << "call Base::show()" << endl;
	}
};

class Derive : public Base
{
public:
	Derive()
	{
		cout << "call Derive()" << endl;
	}
private:
	void show()
	{
		cout << "call Derive::show()" << endl;
	}
};

int main()
{
	Base* pb1 = new Derive();
	pb1->show();
	delete pb1;  //当父类虚函数是public的,子类虚函数即使是private的,也还是可以被调用。因为权限检查在编译器执行,但函数的动态绑定是代码执行时确定的。
	//反之,如果父类虚函数是private的,子类虚函数即使是public的,也不能实现动态绑定,而会因为权限直接编译错误
	return 0;
}
posted @   二氧化硅21  阅读(14)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
点击右上角即可分享
微信分享提示