首页 > 其他分享 >29虚函数-静态绑定-动态绑定

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

时间:2024-01-25 16:56:11浏览次数:49  
标签:函数 show 静态 void 绑定 29 pb Base

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

  • 如果类中定义了虚函数,那么编译阶段,编译器会给这个类类型产生一个唯一的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;
}

标签:函数,show,静态,void,绑定,29,pb,Base
From: https://www.cnblogs.com/sio2zyh/p/17987520

相关文章

  • 静态路由基本配置
    拓扑:配置:查看代码[R1]discurrent-configuration[V200R003C00]#sysnameR1#boardadd0/12SA#snmp-agentlocal-engineid800007DB03000000000000snmp-agent#clocktimezoneChina-Standard-Timeminus08:00:00#portallocal-serverloadportalpage.......
  • 代码随想录 day29 非递减子序列 全排列 全排列 II
    非递减子序列cpp就业还是太难了还是转java吧好歹这个对双非还友好一些尝试写java的第一天本题关键是理解非递减子序列判断条件需要额外一个数组记录当前元素是否在本树层使用过记录在这个数组就说明用过了全排列本题系统的演示了怎么写全排列和最基本的组合问题的......
  • WPF动态绑定隐藏或显示DataGrid一列(转)
    原文连接一、添加一個FrameworkElement的代理<Window.Resources><FrameworkElementx:Key="ProxyElement"DataContext="{Binding}"/></Window.Resources> 二、用一個不可見的ContentControl綁定上一步的FrameworkElement代理<ContentControlV......
  • AT_abc296_d
    题目大意现在有两个数\(n\)和\(m\),请问你是否可以构造出一个数\(x\),使得\(m\lex\),并且让\(x=a\timesb\left(1\lea,b\len\right)\)。思路我们可以枚举数\(a\),判断是否可以使得\(a\timesb=x\)。但是我们可以发现,这样是错误的,因为\(1\len,m\le10^{12}\),并且答案也......
  • 静态区间查询(条件动态)——ST表
    目录问题引入思路一览具体分析条件动态?问题引入给出一个长度为n的数组a,并且给出m咨询问,每次询问给出边间lt和rt,要求给出lt和rt之间的最大值思路一览暴力法:记录数组,对于每一次询问,就从lt到rt遍历一遍ST:对数组的区间做一个倍增处理,将每一个区间的答案记录下来,最后使用区间进行......
  • 内部类(匿名、成员、静态)
    1、匿名内部类匿名内部类在方法中创建,不能用public等来修饰在方法内部使用,此时,只需要声明一个Outer05对象,然后使用f1方法,就可以使用这个匿名内部类 类的匿名内部类,如果去掉大括号中的内容,则变成创建一个Father对象,但是有这个大括号,则是使用一个匿名内部类,如果类本身不是......
  • 【题解 P3293】 美味
    [SCOI2016]美味题目描述一家餐厅有\(n\)道菜,编号\(1,2,\ldots,n\),大家对第\(i\)道菜的评价值为\(a_i\)。有\(m\)位顾客,第\(i\)位顾客的期望值为\(b_i\),而他的偏好值为\(x_i\)。因此,第\(i\)位顾客认为第\(j\)道菜的美味度为\(b_i\oplus(a_j+x_i)\),\(\opl......
  • Linux-unbuntu里静态库、动态库
    静态库:特点:生成的可执行程序复制了一份整个库,以空间换取时间第一步:准备功能函数eg:add.c sub.c  div.c...第二步:把功能函数只编译不链接,得到.o文件gcc-cadd.c-oadd.o第三步:将功能函数的.o文件进行打包成库(打包完成会生成一个.a结尾的库,此库里已经把功能函数都封装进来了)ar......
  • libm.so.6: version `GLIBC_2.29' not found
    基础GLIBC是Linux系统中最底层的API,最主要的功能是对系统调用进行了封装,几乎其他任何的运行库都要依赖glibc。因此,切勿擅自通过编译的方式升级,容易将系统搞坏。升级glibc主要是对/lib库中的libc.so.6,libm.so.6,libpthread.so.0和librt.so.1这四个文件的修改[root@taishan-atlas......
  • CF-292-D-并查集
    292-D题目大意给定一张无向图,由\(n\)个顶点\(m\)条边。有\(q\)次询问,每次询问将\([l,r]\)的边删去,问图中有多少连通分量。Solution涉及连通分量,尝试应用并查集解决。记录一个前缀并查集\(pre[i]\),表示前\(i\)条边连通后的图;和一个后缀并查集\(suf[i]\),表示后\(m-i\)条边连通......