C++ Member Function Pointer

说一说C++的成员函数指针。

在刚学C++的时候,一个基本的概念是指针的大小等于机器的字长。因此在32位的机器上,一个指向整型的指针sizeof会返回4。那么,是不是意味所有类型的指针sizeof后,都是4呢?

一般来说,绝对的事情是比较少的,特殊情况总是存在的,那就是成员函数指针。我们从简单的例子一点一点来看这个问题。

案例一:

class CBase1 {};

typedef void (CBase1::*FPbase1)();

int main()

{

size_t sizeFPbase1 = sizeof(FPbase1);

cout << "sizeFPbase1 = " << sizeFPbase1 << endl;

}

OK,没啥特殊的,输出4。

再来看一个:

class CBase1 {};

class CDerive1 : public CBase1 {};

typedef void (CDerive1::*FPderive1)();

int main()

{

size_t sizeDerive1 = sizeof(FPderive1);

cout << "sizeDerive1 = " << sizeDerive1 << endl;

}

还是4。那什么时候成员函数指针大小才会不等于4呢?请看下面这个例子。

案例三:

class CBase1 {};

class CBase2 {};

class CDerive2 : public CBase1, public CBase2 {};

typedef void (CDerive2::*FPderive2)();

int main()

{

size_t sizeDerive2 = sizeof(FPderive2);

cout << "sizeDerive2 = " << sizeDerive2 << endl;

}

在VC下,你会发现sizeDerive2的大小是8,这是为什么呢?

一般来说,通用的解释就是:“这是有一定历史渊源的”。事实也确实如此。但是历史的事情,我就不说了,大家自己google好了。这里以VC为例,做一个简单介绍(WARNING:GCC等其他编译器在实现上是存在不同的)。

我们知道C++里有一个关键字叫this。对于CDerive1来说,这个类对象的this (简称CDerive1-this)和CBase1 (CBase1-this)是一样的。但是对于CDerive2-this来说,情况有所不同。如果是用来调用CBase1中的成员函数,没什么问题。但是当调用CBase2中的成员函数时,该怎么办?总不能拿CBase1-this来搞吧?

因此,在VC中,这个成员函数指针有两部分组成,一部分是CDerive2-this,还有一部分用于调整CDerive2-this。下面用一个简单的例子来证明下。

案例四:

int main()

{

FPderive2 fpDerive2 = nullptr;

FPbase2 fpBase2 = nullptr;



fpDerive2 = fpBase2;

}

怎么证明呢?先来看汇编吧。

; 46 : FPderive2 fpDerive2 = nullptr;

mov DWORD PTR $T5371[ebp], 0

mov DWORD PTR $T5371[ebp+4], 0


mov ecx, DWORD PTR $T5371[ebp]

mov DWORD PTR _fpDerive2$[ebp], ecx

mov edx, DWORD PTR $T5371[ebp+4]

mov DWORD PTR _fpDerive2$[ebp+4], edx

; 47 : FPbase2 fpBase2 = nullptr;

mov DWORD PTR _fpBase2$[ebp], 0

; 48 :

; 49 : fpDerive2 = fpBase2;

mov eax, DWORD PTR _fpBase2$[ebp]

mov DWORD PTR $T5372[ebp], eax

mov DWORD PTR $T5372[ebp+4], 1

mov ecx, DWORD PTR $T5372[ebp]

mov DWORD PTR _fpDerive2$[ebp], ecx

mov edx, DWORD PTR $T5372[ebp+4]

mov DWORD PTR _fpDerive2$[ebp+4], edx

首先看一下高亮的那几行。很明显地你会发现同样是初始化,fpDerive2和fpBase2产生的汇编码完全两样。fpDerive2占用了2个DWORD区域,而fpBase2只占用了1个DWORD。这里解释了sizeof为什么一个返回8一个返回4。

再看fpDerive2 = fpBase2。

; 49 : fpDerive2 = fpBase2;

mov eax, DWORD PTR _fpBase2$[ebp]

mov DWORD PTR $T5372[ebp], eax

mov DWORD PTR $T5372[ebp+4], 1

mov ecx, DWORD PTR $T5372[ebp]

mov DWORD PTR _fpDerive2$[ebp], ecx


mov edx, DWORD PTR $T5372[ebp+4]

mov DWORD PTR _fpDerive2$[ebp+4], edx

注意高亮部分。这里我们看到,fpDerive2的高DWORD区域被置1了。如果我们的代码是fpDerive2 = fpBase1会是什么情况呢?试一下,你就会发现,fpDerive2的高DWORD区域被置0了。所以,我们有理由相信高DWORD区域是被用来存放一个调节变量的。

那么,低DWORD用来存放什么呢?自己试吧,简单说就是this。

这里,我们考虑的情况还只是多重继承。如果是虚继承又会发生什么情况?如果一个类只有一个前置声明,那么它的成员函数指针大小是多少?这些情况会更复杂。有兴趣的同学可以参考下面几篇文章做更细致的研究。

更新(4/12/2012):研究了下FastDelegates的代码,针对VC,好像我的理解还是有点偏差。
原文链接: https://www.cnblogs.com/wpcockroach/archive/2012/04/05/2441489.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/46118

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 下午10:33
下一篇 2023年2月8日 下午10:34

相关推荐