zoukankan      html  css  js  c++  java
  • C++成员不通过对象调用的直接调用写法

    C++成员不通过对象调用(.或->方式)的另类(C式)调用写法

    #include <iostream>
    using namespace std;
    
    /* 
    我们知道,成员函数和普通函数最大的区别就是成员函数包含一个隐藏的参数this指针,用来表明成员函
    数当前作用在那一个对象实例上。
    
    根据调用约定(Calling Convention)的不同,成员函数实现this指针的方式也不同。
    
    1.    如果使用__thiscall调用约定,那么this指针保存在寄存器ECX中,VC编译器缺省情况下就是这样的。
    2.    如果是__stdcall或__cdecl调用约定,this指针将通过栈进行传递,且this指针是最后一个被压入栈
    的参数,相当于编译器在函数的参数列表中最左边增加了一个this参数。 
    */
    class Base {
    public:
    virtual void f() { cout << "Base::f()" << endl; }
    virtual void g() { cout << "Base::g()" << endl; }
    virtual void h() { cout << "Base::h()" << endl; }
    
    virtual void foo(Base *pThis) { 
    pThis->hello(this); 
    }
    virtual void hello(Base *pThis) { 
    pThis->h();
    }
    
    //virtual void __stdcall hello(Base *pThis) {} //成员函数指定了__stdcall调用约定
    };
    
    int test()
    {
    typedef void(*Fun)();
    
    Base *b = new Base;
    cout << *(int*)(&b) << endl; //虚函数表的地址存放在对象最开始的位置
    
    Fun funf = (Fun)(*(int*)*(int*)b);
    Fun fung = (Fun)(*((int*)*(int*)b + 1));
    Fun funh = (Fun)(*((int*)*(int*)b + 2));
    
    /************************************************************************/
    /*    调用内部无this参与的成员(包括变量和方法)的对象方法    */
    /************************************************************************/
    
    //如果下面三个方法里,没有用到与对象相关的成员可以不用为ecx赋值,否则会出错
    funf();
    fung();
    funh();
    
    
    /************************************************************************/
    /* 调用内部有this参与的成员(包括变量和方法)的对象方法 */
    /************************************************************************/
    
    //少了__stdcall(注意位置),栈会不平衡了:本来c++默认是thiscall,如果不要,vs编译器会让调用者平衡栈,即多了一句 add esp, 4
    typedef void(__stdcall *Fun_Base)(Base*);
    Fun_Base foo = Fun_Base(*((int*)*(int*)b + 3));
    
    //就是多增加这句,因为编译器对c++默认采用thiscall
    _asm{
    mov ecx, dword ptr[b]
    }
    
    foo(b);//里面用到this了,不给ecx赋值,this就不对
    
    
    /************************************************************************/
    /* 调用内部有this参与的成员(包括变量和方法)的对象方法,纯汇编版本    */
    /************************************************************************/
    
    //尝试调用虚函数表的第四个方法
    _asm{
    //同上,如果所call的方法里,没有用到与对象相关的成员可以不用为ecx赋值,否则会出错
    mov ecx, dword ptr[b]
    
    push ecx    //一个入参
    
    //mov ecx, b或mov eax, [b],表达同一个意思,vs最后都是mov ecx, dword ptr[b];
    //实质是mov eax, [EBP-04h],结果表现为ecx=b,即从栈上获取指针b的值,而不是*b(即*(int*)b == ptr_vftable)的值。
    mov eax, [b]    //获取对象指针
    mov eax, [eax]    //虚函数表首地址,即对象的开始处,ptr_vftable = [eax + 0] = [ecx] = [this_of_b] = [b]
    call [eax + 0x0c]    //调用虚函数表的第四个,某虚函数表里第N个方法(x86),[ptr_vftable + (N - 1) * 4] = [[eax]+(N-1)*4]
    }
    
    /************************************************************************/
    /* 正常的调用 */
    /************************************************************************/
    
    /*
    00EF9886 8B 45 F4 mov eax, dword ptr[b]
    00EF9889 50 push eax
    00EF988A 8B 4D F4 mov ecx, dword ptr[b]
    00EF988D 8B 11 mov edx, dword ptr[ecx]
    00EF988F 8B 4D F4 mov ecx, dword ptr[b]
    00EF9892 8B 42 0C mov eax, dword ptr[edx + 0Ch]
    00EF9895 FF D0 call eax
    */
    b->foo(b);
    
    //http://blog.csdn.net/haoel/article/details/1948051/
    /*
    虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。
    这个结束标志的值在不同的编译器下是不同的。
    
    1. 在WinXP+VS2003下,这个值是NULL。
    2. 而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,
    2.1 这个值是如果1,表示还有下一个虚函数表,
    2.2 如果值是0,表示是最后一个虚函数表。
    */
    cout << (Fun)(*((int*)*(int*)b + 5)); // 最后一个位置为0,表明虚函数表的结束
    
    return 0;
    }

    C++虚函数介绍

    1. 先从C++看看下虚函数表和多态表现 http://blog.csdn.net/haoel/article/details/1948051/

    2. 有虚函数的类的大小 http://blog.csdn.net/hackbuteer1/article/details/7883531

    3. 再从汇编看,类的继承,虚函数表的形成,表放在哪个地址等 http://www.pediy.com/kssd/pediy10/60538.html

    虚函数表的形成,是在类构造函数里,对实例对象首地址里存放的虚函数数组进行修改实现的。

    class Child :public Base{
    public:
    virtual void vf1(){cout<<"I'm in sub Class.";}
    virtual void vf2(){cout<<"I'm in sub Class.";}
    //...
    }
    .text                     .rdata?
    class Child内存           这个类的所有对象所共有的
    +-------------+          +-----+-----+-----+-----+----+
    | ptr_vftable | -------> | vf1 | vf2 | vf3 | ... |end?|
    +-------------+          +-----+-----+-----+-----+----+
    |             |
    |other members|
    |             |
    +-------------+

    逆向C++(中文版)
    http://wenku.baidu.com/link?url=bjLVj2eqfe29_Edzi99MBGJeoCtVaHDXj-3r4s4lm771BAQnJ0WIUaQywPZgGq3Yz_uU9yh-B0V6q5SFMUhRo0t436BUnUdaHuhpwERvLvC

    C++多态性:

    GoF著作中未提到的设计模式(4):Double Dispatch
    http://www.cnblogs.com/west-link/archive/2011/07/26/2116887.html
    http://en.wikipedia.org/wiki/Double_dispatch

  • 相关阅读:
    网站微信登录-python 实现
    最终还是迁移到github
    理解 python 装饰器
    Git 学习之 Git Basics
    haskell 常用 函数
    jinja 2 filter 使用
    目标的牵引作用
    姨妈去世了
    OKR的理解
    无法证明的事情,和梦又有什么区别呢
  • 原文地址:https://www.cnblogs.com/Fang3s/p/4486538.html
Copyright © 2011-2022 走看看