class B {
public:
virtual void foo() {}
};
class D: public B {
public:
D() : mA(0) {}
virtual void foo() {
cout<<"D::foo::mA "<<mA<<endl;
}
int mA;
};
int main() {
D d1;
D* pD = &d1;
cout<<pD<<endl;
typedef void (*PFun)();
PFun fun = (PFun)((long *)*((long *)*(long*)(pD)));
fun();
cout<<"D::pD::mA: "<<pD->mA<<endl;
}
为啥mA
输出的值不一样?
1
phttc 2018-08-21 17:21:28 +08:00
我拿来跑了一下,输出一样的。。
|
2
HackerPainter OP @phttc 你用的 32 位机器吧,64 位机器是不一样的,我用 linux 服务器和 mac 都试过了,是不一样的
|
3
cgsv 2018-08-21 17:30:07 +08:00
typedef void (*PFun)(void* self);
PFun fun = (PFun)((long *)*((long *)*(long*)(pD))); fun(pD); |
4
HarveyDent 2018-08-21 17:30:53 +08:00 1
你这个是未定义行为啊,不同编译器肯定不同。
如果你非要这么干的话,类成员函数应该是需要一个 this 指针的,这样改一下在我的环境 gcc 能得到一样的结果了: typedef void (*PFun)(D* p); PFun fun = (PFun)((long *)*((long *)*(long*)(pD))); fun(pD); 同样的环境: fun(0); 我获得了一个 Segmentation fault,也是符合预期的。 如果你想探究一下编译器怎么实现虚函数表的,可以试着玩一下。 |
5
wevsty 2018-08-21 17:32:04 +08:00
我十分想请楼主解释一下这行是啥意思。
PFun fun = (PFun)((long *)*((long *)*(long*)(pD))); pD 是一个指向 class D 的指针,pD 转换为一个 long 型的指针以后再对他解引用是个什么操作?解引用出来的东西又强制解释为指针第二次解引用,最后还要转换成函数指针又是个什么操作,看不懂。 |
6
HackerPainter OP @cgsv 能说明一下原因吗?我只知道 c++成员函数扩展后第一个参数是对象 this,不知道函数指针 PFun 带参数与这有啥关系
|
7
HackerPainter OP @wevsty 直接通过虚函数表指针调用函数
|
8
HackerPainter OP @HarveyDent 明白了,thks
|
9
wevsty 2018-08-21 17:43:13 +08:00 1
@HackerPainter
虚函数表是编译器决定怎么实现的,这样子不能保证行为。 如果要调用类的成员函数,即使成员函数不需要参数,成员函数的第一个参数仍然 this 指针,并不是空参数。 在 MSVC X64 的编译器下面,你这代码附带一个编译警告 C4312,运行直接崩。 原因是 MSVC X64 的 long 是 32 位的,而 long*是 64 位的。 |
10
gnaggnoyil 2018-08-21 18:04:52 +08:00
* `void ()`
* `void (D::)()` * `long` 这三个类型之间两两相互不 type aliasing/pointer interchangeable,LZ 你自己数数自己触发了多少未定义行为…… |
11
HackerPainter OP @gnaggnoyil 没有触发,gcc 都能正常编译
|
12
yanxijian 2018-08-21 19:01:10 +08:00 via iPhone
磨练技术也不用写这种代码吧。工作中遇到直接打死😏
|
13
HackerPainter OP @yanxijian 工作中一些大神将函数指针用的神乎其技,没办法
|
14
GeruzoniAnsasu 2018-08-21 20:00:12 +08:00
写这种东西你需要同时对照
[Intel 开发人员手册]( https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf) [msdn]( https://docs.microsoft.com/zh-cn/cpp/build/x64-software-conventions) 以及[C++标准]( https://zh.cppreference.com/w/cpp/language/ub) 才写的出能跑的东西 |
15
geelaw 2018-08-21 20:05:56 +08:00
有些编译器实现的虚函数指针的长度是普通函数指针的两倍,似乎有虚拟继承的原因。不要这么做。
@HackerPainter #13 请你确保你是否在使用 COM,因为 COM 规定了接口方法必须以某种方式实现,那样才能确保这样的代码是可以工作的(在第一个参数放了 this 之后)。如果只是随便一个 C++ 的虚函数,这样做无法保证有任何好下场。 |
16
eastera 2018-08-21 22:24:09 +08:00
看编译器怎么做的,虚函数表没有要求,不同编译器结果可能不一样
|
17
lychnis 2018-08-22 01:52:55 +08:00
上面解释的很清楚了 这种代码绝对不允许出现在 svn 里面 只能自己玩
|
18
bilosikia 2018-08-22 10:16:52 +08:00
#include <iostream>
using namespace std; class B { public: virtual void foo() {} }; class D: public B { public: D() : mA(888) {} virtual void foo() { cout<<"D::foo::mA "<<mA<<endl; } int mA; }; int main() { D d1; D* pD = &d1; cout<<pD<<endl; typedef void (*PFun)(D *a); PFun fun = (PFun)((long *)*((long *)*(long*)(pD))); fun(pD); cout<<"D::pD::mA: "<<pD->mA<<endl; } 这样就是一样的了, 你不需要传 this 的吗 |
19
qinyusen 2018-08-22 12:19:13 +08:00
工作中直接打死+1
如果是日常爱好,请使用 GDB 单步调试,看一下所有的地址就好了。 |
20
qinyusen 2018-08-22 12:22:24 +08:00
@HackerPainter 工作中的大神,应该写的是谁都能维护的“蠢”代码,但是一样结构合理思路清晰。
你这是炫技的大神,工作中,这种 code 因为可维护性为 0,除非是需要极致性能的情况下,否则,就是一棒子打死,reviewer 会勒令整改的。 多少个 ACM 出身的同学都是因为写炫技代码被 leader 拍死的。。。 |