求助 C++大神看一个问题 - V2EX
HackerPainter
V2EX    C

求助 C++大神看一个问题

  •  
  •   HackerPainter Aug 21, 2018 4745 views
    This topic created in 2825 day ago, the information mentioned may be changed or developed.
    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输出的值不一样?

    20 replies    2018-08-22 12:22:24 +08:00
    phttc
        1
    phttc  
       Aug 21, 2018
    我拿来跑了一下,输出一样的。。
    HackerPainter
        2
    HackerPainter  
    OP
       Aug 21, 2018
    @phttc 你用的 32 位机器吧,64 位机器是不一样的,我用 linux 服务器和 mac 都试过了,是不一样的
    cgsv
        3
    cgsv  
       Aug 21, 2018
    typedef void (*PFun)(void* self);
    PFun fun = (PFun)((long *)*((long *)*(long*)(pD)));
    fun(pD);
    HarveyDent
        4
    HarveyDent  
       Aug 21, 2018   1
    你这个是未定义行为啊,不同编译器肯定不同。

    如果你非要这么干的话,类成员函数应该是需要一个 this 指针的,这样改一下在我的环境 gcc 能得到一样的结果了:
    typedef void (*PFun)(D* p);
    PFun fun = (PFun)((long *)*((long *)*(long*)(pD)));
    fun(pD);

    同样的环境:
    fun(0);
    我获得了一个 Segmentation fault,也是符合预期的。

    如果你想探究一下编译器怎么实现虚函数表的,可以试着玩一下。
    wevsty
        5
    wevsty  
       Aug 21, 2018
    我十分想请楼主解释一下这行是啥意思。
    PFun fun = (PFun)((long *)*((long *)*(long*)(pD)));
    pD 是一个指向 class D 的指针,pD 转换为一个 long 型的指针以后再对他解引用是个什么操作?解引用出来的东西又强制解释为指针第二次解引用,最后还要转换成函数指针又是个什么操作,看不懂。
    HackerPainter
        6
    HackerPainter  
    OP
       Aug 21, 2018
    @cgsv 能说明一下原因吗?我只知道 c++成员函数扩展后第一个参数是对象 this,不知道函数指针 PFun 带参数与这有啥关系
    HackerPainter
        7
    HackerPainter  
    OP
       Aug 21, 2018
    @wevsty 直接通过虚函数表指针调用函数
    HackerPainter
        8
    HackerPainter  
    OP
       Aug 21, 2018
    @HarveyDent 明白了,thks
    wevsty
        9
    wevsty  
       Aug 21, 2018   1
    @HackerPainter
    虚函数表是编译器决定怎么实现的,这样子不能保证行为。
    如果要调用类的成员函数,即使成员函数不需要参数,成员函数的第一个参数仍然 this 指针,并不是空参数。

    在 MSVC X64 的编译器下面,你这代码附带一个编译警告 C4312,运行直接崩。
    原因是 MSVC X64 的 long 是 32 位的,而 long*是 64 位的。
    gnaggnoyil
        10
    gnaggnoyil  
       Aug 21, 2018
    * `void ()`
    * `void (D::)()`
    * `long`
    这三个类型之间两两相互不 type aliasing/pointer interchangeable,LZ 你自己数数自己触发了多少未定义行为……
    HackerPainter
        11
    HackerPainter  
    OP
       Aug 21, 2018
    @gnaggnoyil 没有触发,gcc 都能正常编译
    yanxijian
        12
    yanxijian  
       Aug 21, 2018 via iPhone
    磨练技术也不用写这种代码吧。工作中遇到直接打死
    HackerPainter
        13
    HackerPainter  
    OP
       Aug 21, 2018
    @yanxijian 工作中一些大神将函数指针用的神乎其技,没办法
    GeruzoniAnsasu
        14
    GeruzoniAnsasu  
       Aug 21, 2018
    geelaw
        15
    geelaw  
       Aug 21, 2018
    有些编译器实现的虚函数指针的长度是普通函数指针的两倍,似乎有虚拟继承的原因。不要这么做。

    @HackerPainter #13 请你确保你是否在使用 COM,因为 COM 规定了接口方法必须以某种方式实现,那样才能确保这样的代码是可以工作的(在第一个参数放了 this 之后)。如果只是随便一个 C++ 的虚函数,这样做无法保证有任何好下场。
    eastera
        16
    eastera  
       Aug 21, 2018
    看编译器怎么做的,虚函数表没有要求,不同编译器结果可能不一样
    lychnis
        17
    lychnis  
       Aug 22, 2018
    上面解释的很清楚了 这种代码绝对不允许出现在 svn 里面 只能自己玩
    bilosikia
        18
    bilosikia  
       Aug 22, 2018
    #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 的吗
    qinyusen
        19
    qinyusen  
       Aug 22, 2018
    工作中直接打死+1

    如果是日常爱好,请使用 GDB 单步调试,看一下所有的地址就好了。
    qinyusen
        20
    qinyusen  
       Aug 22, 2018
    @HackerPainter 工作中的大神,应该写的是谁都能维护的“蠢”代码,但是一样结构合理思路清晰。

    你这是炫技的大神,工作中,这种 code 因为可维护性为 0,除非是需要极致性能的情况下,否则,就是一棒子打死,reviewer 会勒令整改的。

    多少个 ACM 出身的同学都是因为写炫技代码被 leader 拍死的。。。
    About     Help     Advertise     Blog     API     FAQ     Solana     950 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 59ms UTC 22:58 PVG 06:58 LAX 15:58 JFK 18:58
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86