关于typedef
时间:2011-12-05
来源:互联网
Base b;
Fun pFun = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
// Invoke the first virtual function
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
今天看了一个大牛的blog讲虚函数的 大概的理论有了一定的了解
但是我对其中的一个基础不是很了解以上是我了解的片断
typedef void(*Fun)(void);这句是定义了什么搞不明白所以Fun pFun = NULL就更不明白了
pFun = (Fun)*((int*)*(int*)(&b));这句的(Fun)*((int*)*(int*)(&b))是个什么意思
谢谢大家了
作者: CDERFV 发布时间: 2011-12-05
Fun pFun = NULL; pFun这个无参数无返回值的函数指针初始化为空
pFun = (Fun)*((int*)*(int*)(&b));虚函数表 — 第一个函数地址并且强制类型转换为
无参数无返回值的函数指针类型赋给pFun
作者: Rotaxe 发布时间: 2011-12-05
typedef void(*Fun)(void);定义了一个返回类型为void,参数为void的指针类型. Fun pFun = NULL;定义了该类型的一个指针并初始化为NULL. (int*)(&b)将对象b的首地址转化为整型指针. *(int*)(&b)取该对象本身第一个元素,一般为虚表指针,指向虚函数首地址. (int*)*(int*)(&b)将虚表指针转化为整型指针类型. *(int*)*(int*)(&b)函数首地址. pFun = (Fun)*(int*)*(int*)(&b)转化为定义的函数类型并赋值给pFun.
作者: qwer_boo 发布时间: 2011-12-05
Fun是函数指针类型名
所以可以用它来定义变量
Fun pFun = NULL;
这句就是定义了一个函数指针类型的变量pFun,初始化为NULL
pFun = (Fun)*((int*)*(int*)(&b));
这个真是超级无聊,从右到左依次分析
&b取地址
(int*)(&b)强制转换成int*指针
*(int*)(&b)解引用
(int*)*(int*)(&b)强转,转成int*指针
(Fun)*((int*)*(int*)(&b))继续强转,转成函数指针Fun类型指针
可以无视这段代码,很无聊
作者: qscool1987 发布时间: 2011-12-05
typedef void(*Fun)(void);//定义函数指针
这个函数指针,this指针都没传递,就算得到了类的非静态函数地址,也不可能调用成功啊。
没有编译器啊,不然试一下就知道了。
cout << "虚函数表地址:" << (int*)(&b) << endl; //类的首地址 如果类有虚函数的话
VC编译器会为类隐式添加一个指针,这个指针指向虚函数表,并且这个指针为类实例化数据的首个地址。
(int*)应该是为了使用cout而使用的强制转化
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;//*(int*)(&b)
对(int*)(&b)解引用获得函数地址,最前面这句(int*)同上。
作者: mymixing 发布时间: 2011-12-05
但看的出来,写这段代码的人应该对类的理解已经很深刻了。
不过光靠看的话,还是觉得直接使用函数指针去指向类的非静态成员函数时,
调用会以失败告终。
作者: mymixing 发布时间: 2011-12-05
是说定义了一个新的类型Fun 这个类型是一个函数指针类型,它的参数表和返回值都是void(空)
Fun pFun = NULL
申明了一个新的函数指针pFun 它是Fun类型的,也就是上面定义的,也就是说pFun 所指向的函数将必须满足形参表和返回值都是void;
紧接着将pFun初始化为NULL,也就是咱是不指向明确的一个函数,可能之后再定位
//这是一个好的习惯,申明任何类型的指针式都初始化
pFun = (Fun)*((int*)*(int*)(&b))
也就是给pFun赋值了
赋的是什么值呢? 主要就是星号*看不明白对吧,
在这一行中有2种星号,一种是紧跟类型的 如int * 表示指向int的指针类型;另一种是单独出现的* 这样的星号就是运算符了,C++中*作运算符又分一元运算法和二元运算符,一元时表示解析运算(也叫解引用运算,即从某个地址反向解析出里面的值,解析方式与那个地址的类型有关),二元时表示相乘.
显然,这里没有二元运算,全是一元的.C++中*运算结合型为右结合,因此从右往左读.
1)首先&b为对象b的地址,根据虚函数的编译原理,在每个对象的内存空间里首先存放虚函数表的地址,我们的任务就是定位这个地址,如果我们直接对&b解引用,即*(&b),根据解引用规则,将返回对b本身的引用,因为&b表示的地址是Base类的地址.这样显然与我们的意图不和.
2)为了定位虚函数表的地址,我们将&b的类型转换为单行地址类型.这里用到一个小技巧,在计算机中,一个整形类型的长度必然等于单位地址的长度(任意类型不管本身占用多少内存,它的地址占用的内存都是固定的),在32位计算机中都是32位,4个字节.因此,(int *)(&b)就将同一个类类型的地址转换为一个单位地址类型.
3)我们将&b转换为int * 并不是说将b转化为int,只是使之后的解引用操作能正确定位.计算机执行程序时也不会知道我们这么做会查到什么内容,但它会严格按照我们的要求去查找,也就是将&b表示的地址进行解引用,并在寻到地址之后取前4个字节的内容.
4)如前所述*(int*)(&b)就通过巧妙地改变地址的类型,获得了新的内容,也就是内存中&b开始之后的4个字节中的内容.我们知道这就是虚函数表的地址.
5)我们为了进一步从虚函数表的地址定位虚函数需要进一步解析之,然而,我们之前说了3)中(int *)已经告诉计算机解引用之后应该是一个int类型的数.而不是地址的东西是不可以解析操作的.因此我们再将这个数转换成一个地址(从语法上说就是int -> int *)也许你会表示诧异,一个int数怎么可以转换成一个地址呢?其实我们只是运用语言的规则罢了,我们只要事先知道我们的行为不会引起异常,就可以将操作告诉计算机,他自然会去执行,当然后果由我们去负责.
6)((int*)*(int*)(&b))就是我们最后得到的地址,将之解引用(这时终于可以了,*((int*)*(int*)(&b)))得到了一个32位的数据,根据理论,我们知道这时虚函数表的第一项.这一项中实际保存的就是第一项虚函数的地址(函数指针),ok将之转化成Fun类型即可也就是,明确这个函数指针所指向的函数,其形参表和返回值都是void;
7)最后赋给我们声明并初始化为NULL的变量pFun.
讲的有点多,可能涉及很多底层的东西,也有可能讲错的,希望大家指正
作者: wjkdtctorrent 发布时间: 2011-12-05
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28