89 C++对象模型探索。数据语义学 - 虚成员函数,静态成员函数调用方式

虚函数也是在编译阶段就确定了固定的地址,本质上通过this指针去调用。加上是否需要通过vptr指针去查找虚函数表,找到真正的地址。

零:验证虚函数也是在编译阶段就固定了地址,打印虚函数指针的方法在window上和linux上不一样。

其他成员函数,全局函数,静态函数打印地址的的方式和window上一样,在linux上也一样。

window下打印:编译后打印多次都是同一个地址。

    //打印虚函数的地址
    printf("Teacher21:;func1的地址是%p\n",&Teacher21::func1);
    cout << &Teacher21::func1 << endl;//使用cout打印alway是1,这样不行

	//打印虚函数的地址
	printf("Teacher21:;func1的地址是%p\n",&Teacher21::func1);
	cout << &Teacher21::func1 << endl;//使用cout打印alway是1,这样不行

在linux 上的写法为:

	Teacher21 tea89;
	void (Teacher21::*vfp)() = &Teacher21::func1;
	printf("address:%p \n",(void *)(tea89.*vfp) );

一,虚成员函数调用方法

通过前面的知识,我们知道,虚成员函数的调用 ,在类对象类对象指针/类对象引用 时是不一样的。

如果是对象调用,则是通过静态绑定的方式,是和普通成员函数的调用一样的。

class Teacher21 {

public:
	void virtual func1() {
		cout << "Teacher21 vir func1 called this = " << this << endl;
	}

};

void main() {
	Teacher21 tea;
	tea.func1();
	cout << "======" << endl;
	Teacher21 *ptea = new Teacher21();
	ptea->func1();

}

如果是指针或者引用调用,则是动态绑定的方式,是要通过 vptr找到虚函数表中的真正的地址,然后调用

验证此说法:上述代码,debug查看反汇编。可以看到指针的调用,做了很多事情,实际上就是通过vptr查找虚函数表,然后找到真正的地址,然后再调用。

    tea.func1();
0081AF2A  lea         ecx,[tea]  
0081AF2D  call        Teacher21::func1 (08117D5h)

    ptea->func1();
0081AFA4  mov         eax,dword ptr [ptea]  
0081AFA7  mov         edx,dword ptr [eax]  
0081AFA9  mov         esi,esp  
0081AFAB  mov         ecx,dword ptr [ptea]  
0081AFAE  mov         eax,dword ptr [edx]  
0081AFB0  call        eax  
0081AFB2  cmp         esi,esp  
0081AFB4  call        __RTC_CheckEsp (08114BAh)  

虚函数的调用除了上述操作外,也会加一个this指针。

在虚函数中调用虚函数的情况分析

class Teacher21 {

public:
	void virtual func1() {
		cout << "Teacher21 vir func1 called this = " << this << endl;
		func2();
	}

	void virtual func2() {
		cout << "Teacher21 vir func2 called this = " << this << endl;
	}

};

void main() {
	Teacher21 tea;
	tea.func1();
	cout << "======" << endl;
}

在调用     tea.func1();时debug ,查看反汇编代码,看到,

在虚函数中直接调用虚函数,也会使用vptr指针的方式,去查找,反汇编代码如下:

	void virtual func2() {
00A38900  push        ebp  
00A38901  mov         ebp,esp  
00A38903  sub         esp,0CCh  
00A38909  push        ebx  
00A3890A  push        esi  
00A3890B  push        edi  
00A3890C  push        ecx  
00A3890D  lea         edi,[ebp-0CCh]  
00A38913  mov         ecx,33h  
00A38918  mov         eax,0CCCCCCCCh  
00A3891D  rep stos    dword ptr es:[edi]  
00A3891F  pop         ecx  
00A38920  mov         dword ptr [this],ecx  
00A38923  mov         ecx,offset _60E5D22F_consoleapplication2@cpp (0A4C028h)  
00A38928  call        @__CheckForDebuggerJustMyCode@4 (0A314A1h)  
		cout << "Teacher21 vir func2 called this = " << this << endl;

那么怎么样  在虚函数中调用虚函数 能使用虚函数指针和虚函数表呢?

替换成如下的代码就好了

Teacher21::func2();

class Teacher21 {

public:
	void virtual func1() {
		cout << "Teacher21 vir func1 called this = " << this << endl;
		Teacher21::func2();
	}

	void virtual func2() {
		cout << "Teacher21 vir func2 called this = " << this << endl;
	}

};

void main() {
	Teacher21 tea;
	tea.func1();
	cout << "======" << endl;
}

这么改有啥实际意义吗?--是有的

明显我们看到,如果您的代码使用多态调用到func1了,那么您的代码当前的this对象肯定是Teacher21,不可能是Teacher21的子类或者Teacher21 的父类,因此这么调用是有实际意义的,可以节省时间。

二,静态成员函数调用方法

静态成员函数调用的4种方式

    //static 函数
    Teacher21::funcstatic();
    tea.funcstatic();
    ptea->funcstatic();
	((Teacher21 *)0)->funcstatic();
    //如上4行,在编译器眼中是一样的。通过反编译可以证明这一点
    //静态函数可以看成是全局函数。也是在编译阶段就有了明确的地址的

//static 函数
	Teacher21::funcstatic();
00379486  call        Teacher21::funcstatic (03717E4h)  
	tea.funcstatic();
0037948B  call        Teacher21::funcstatic (03717E4h)  
	ptea->funcstatic();
00379490  call        Teacher21::funcstatic (03717E4h)  
	((Teacher21 *)0)->funcstatic();
00379490  call        Teacher21::funcstatic (03717E4h)  
	//如上4行,在编译器眼中是一样的。通过反编译可以证明这一点
	//静态函数可以看成是全局函数。也是在编译阶段就有了明确的地址的

静态成员函数特性:

静态成员没有this指针,

无法直接存取类中的普通的非静态成员变量。

静态成员函数不能再屁股后面使用const,也不能设置为virtual

可以用类对象调用,也可以用类直接调用

静态成员函数等同于全局函数,因此,有需要提供回调函数的这种场合,可以将静态成员函数作为回调函数。

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值