zoukankan      html  css  js  c++  java
  • 理解数据成员指针、函数成员指针

    转自:http://www.cnblogs.com/malecrab/p/5572119.html

    1. 数据成员指针

    对于普通指针变量来说,其值是它所指向的地址,0表示空指针。

    而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始地址的偏移值,空指针用-1表示。例:

    代码示例:

    复制代码
    struct X {
        int a;
        int b;
    };
    #define VALUE_OF_PTR(p)     (*(long*)&p)
    int main() {
        int X::*p = 0;  // VALUE_OF_PTR(p) == -1
        p = &X::a;      // VALUE_OF_PTR(p) == 0
        p = &X::b;      // VALUE_OF_PTR(p) == 4
        return 0;
    }
    复制代码

    2. 函数成员指针

    函数成员指针与普通函数指针相比,其size为普通函数指针的两倍(x64下为16字节),分为:ptr和adj两部分。

    (1) 非虚函数成员指针

    ptr部分内容为函数指针(指向一个全局函数,该函数的第一个参数为this指针),adj部分始终为0。例:

    代码示例:

    复制代码
    extern "C" int printf(const char*, ...);
    
    struct B {
        void foo() {  printf("B::foo(): this = 0x%p
    ", this); }
    };
    struct D : public B {
        void bar() { printf("D::bar(): this = 0x%p
    ", this); }
    };
    void (B::*pbfoo)() = &B::foo; // ptr: points to _ZN1B3fooEv, adj: 0
    void (D::*pdfoo)() = &D::foo; // ptr: points to _ZN1B3fooEv, adj: 0
    void (D::*pdbar)() = &D::bar; // ptr: points to _ZN1D3barEv, adj: 0
    
    extern "C" void _ZN1B3fooEv(B*);
    extern "C" void _ZN1D3barEv(D*);
    #define PART1_OF_PTR(p)     (((long*)&p)[0])
    #define PART2_OF_PTR(p)     (((long*)&p)[1])
    
    int main() {
        printf("&B::foo->ptr: 0x%lX
    ", PART1_OF_PTR(pbfoo));
        printf("&B::foo->adj: 0x%lX
    ", PART2_OF_PTR(pbfoo));    // 0
        printf("&D::foo->ptr: 0x%lX
    ", PART1_OF_PTR(pdfoo));
        printf("&D::foo->adj: 0x%lX
    ", PART2_OF_PTR(pdfoo));    // 0
        printf("&D::bar->ptr: 0x%lX
    ", PART1_OF_PTR(pdbar));
        printf("&D::bar->adj: 0x%lX
    ", PART2_OF_PTR(pdbar));    // 0
    
        D* d = new D();
        d->foo();
        _ZN1B3fooEv(d); // equal to d->foo()
        d->bar();
        _ZN1D3barEv(d); // equal to d->bar()
        return 0;
    }
    复制代码

    (2) 虚函数成员指针

    ptr部分内容为虚函数对应的函数指针在虚函数表中的偏移地址加1(之所以加1是为了用0表示空指针),而adj部分为调节this指针的偏移字节数。例:

    说明:

    • A和B都没有基类,但是都有虚函数,因此各有一个虚函数指针(假设为vptr)。
    • C同时继承了A和B,因此会继承两个虚函数指针,但是为了节省空间,C会与主基类A公用一个虚函数指针(即上图中vptr1),继承自B的虚函数指针假设为vptr2。
    • C没有重写继承自A和B的虚函数,因此在C的虚函数表中存在A::foo和B::bar函数指针(如果C中重写了foo(),则C的虚函数表中A::foo会被替换为C::foo)。
    • C中有两个虚函数指针vptr1和vptr2,相当于有两张虚函数表。
    • A::foo(C::foo)、B::Bar(C::bar)都在虚函数表中偏移地址为0的位置,因此ptr为1(0+1=1)。而C::quz在偏移为8的位置,因此ptr为9(8+1=9)。
    • 当我们使用pc调用C::bar()时,如:“(pc->*pcbar)()”,实际上调用的是B::bar()(即_ZN1B3barEv(pc)),pc需要被转换为B*类型指针,因此需要对this指针进行调节(调节至pb指向的地址),因此adj为8。

    代码示例:

    复制代码
    extern "C" int printf(const char*, ...);
    
    struct A {
        virtual void foo() { printf("A::foo(): this = 0x%p
    ", this); }
    };
    struct B {
        virtual void bar() { printf("B::bar(): this = 0x%p
    ", this); }
    };
    struct C : public A, public B {
        virtual void quz() { printf("C::quz(): this = 0x%p
    ", this); }
    };
    
    void (A::*pafoo)() = &A::foo;   // ptr: 1, adj: 0
    void (B::*pbbar)() = &B::bar;   // ptr: 1, adj: 0
    void (C::*pcfoo)() = &C::foo;   // ptr: 1, adj: 0
    void (C::*pcquz)() = &C::quz;   // ptr: 9, adj: 0
    void (C::*pcbar)() = &C::bar;   // ptr: 1, adj: 8
    
    #define PART1_OF_PTR(p)     (((long*)&p)[0])
    #define PART2_OF_PTR(p)     (((long*)&p)[1])
    int main() {
        printf("&A::foo->ptr: 0x%lX, ", PART1_OF_PTR(pafoo));   // 1
        printf("&A::foo->adj: 0x%lX
    ", PART2_OF_PTR(pafoo));   // 0
        printf("&B::bar->ptr: 0x%lX, ", PART1_OF_PTR(pbbar));   // 1
        printf("&B::bar->adj: 0x%lX
    ", PART2_OF_PTR(pbbar));   // 0
        printf("&C::foo->ptr: 0x%lX, ", PART1_OF_PTR(pcfoo));   // 1
        printf("&C::foo->adj: 0x%lX
    ", PART2_OF_PTR(pcfoo));   // 0
        printf("&C::quz->ptr: 0x%lX, ", PART1_OF_PTR(pcquz));   // 9
        printf("&C::quz->adj: 0x%lX
    ", PART2_OF_PTR(pcquz));   // 0
        printf("&C::bar->ptr: 0x%lX, ", PART1_OF_PTR(pcbar));   // 1
        printf("&C::bar->adj: 0x%lX
    ", PART2_OF_PTR(pcbar));   // 8
        return 0;
    }
    复制代码
  • 相关阅读:
    疫情环境下的网络学习笔记 python 5.8 数据库入门终章
    疫情环境下的网络学习笔记 python 5.7 navicat数据库,例题,sql注入
    疫情环境下的网络学习笔记 python 5.6 暂时看看
    疫情环境下的网络学习笔记 python 5.5 MYSql 表关系,外键
    疫情环境下的网络学习笔记 python 5.4 数据库基础
    疫情环境下的网络学习笔记 python 4.30 初识数据库
    疫情环境下的网络学习笔记 python 4.29 网络小项目
    XJOI 夏令营501-511测试11 游戏
    XJOI 夏令营501-511测试11 统计方案
    CF1197D Yet Another Subarray Problem
  • 原文地址:https://www.cnblogs.com/Allen-rg/p/6927477.html
Copyright © 2011-2022 走看看