zoukankan      html  css  js  c++  java
  • 实战C++对象模型之成员函数调用

     

    先说结论:C++的类成员函数和C函数实质是一样的,只是C++类成员函数多了隐藏参数this

    通过本文的演示,可以看见这背后的一切,完全可C函数方式调用C++类普通成员函数和C++类虚拟成员函数。

     

    为了实现C函数方式调用C++类成员函数,准备两个文件:。

    1) 被调用的C++类成员函数源代码文件aaa.cpp

    #include <stdio.h> // fprintf

     

    class X

    {

    public:

        void xxx();

     

    private:

        int m;

        int n;

    };

     

    void X::xxx() // bbb.cpp完全以C函数方式调用类X的成员函数xxx

    {

        printf("m=%d, n=%d ", m, n);

    }

     

    aaa.cpp编译成共享库:

    $ g++ -g -o libaaa.so aaa.cpp -fPIC -shared

     

    2) 调用的C++类成员函数源代码文件bbb.cpp

    #include <dlfcn.h> // dlopen

    #include <libgen.h> // basename

    #include <stdio.h> // fprintf

    #include <stdlib.h> // exit

    #include <string.h> // strdup

     

    struct X // 对应于aaa.cpp中类X

    {

        int m;

        int n;

    };

     

    // 定义C风格函数指针XXX,使用前让它指向类X的成员函数xxx

    typedef void (*XXX)(struct X*); // 参数实为aaa.cpp中类X的this指针

     

    // 需要指定一个命令行参数argv[1],

    // 值为aaa.cpp中类X的成员函数xxx的名字,

    // 因为C++编译器会对类X的成员函数xxx名字编码,所以实际名字不会是xxx,

    // 本文测试环境xxx编码后的名为_ZN1X3xxxEv,

    // 不同环境可能有区别,特别是不同编译器通常不同,因为C++标准未对这个做规范。

    int main(int argc, char* argv[])

    {

        char* prog = strdup(argv[0]);

        if (argc != 2) {

            fprintf(stderr, "Usage: %s symbol-name ", basename(prog));

            exit(1);

        }

     

        // 为测试方便,两个文件放在同一目录下,省去设置LD_LIBRARY_PATH

        const char* so = "./libaaa.so";

        void* h = dlopen(so, RTLD_NOW); // 加载类X所在共享库文件

        if (NULL == h) {

            fprintf(stderr, "dlopen %s failed: %s ", so, dlerror());

            exit(1);

        }

     

        XXX xxx = (XXX)dlsym(h, argv[1]); // 取和类X的类成员函数xxx的函数地址,以便可以调用它

        if (NULL == xxx) {

            fprintf(stderr, "dlsym %s failed: %s ", argv[1], dlerror());

            exit(1);

        }

     

        // 第1组测试数据

        struct X x1;

        x1.m = 19;

        x1.n = 18;

        (*xxx)(&x1); // 这里完全以C函数方式调用类X的类成员函数xxx

     

        // 第2组测试数据

        struct X x2;

        x2.m = 2019;

        x2.n = 2018;

        (*xxx)(&x2); // 这里完全以C函数方式调用类X的类成员函数xxx

     

        // 第3组测试数据

        x2.m = 29;

        x2.n = 28;

        (*xxx)(&x2); // 这里完全以C函数方式调用类X的类成员函数xxx

     

        return 0;

    }

     

    bbb.cpp编译成可执行程序:

    $ g++ -g -o bbb bbb.cpp -ldl

     

    执行bbb,看看效果,运行结果和预计完全一致:

    $ ./bbb _ZN1X3xxxEv

    m=19, n=18

    m=2019, n=2018

    m=29, n=28

     

    以更优雅方式运行:

    $ ./bbb `nm libaaa.so | awk /xxx/'{print $3}'`

    m=19, n=18

    m=2019, n=2018

    m=29, n=28

     

    对于类虚拟成员函数,做法是一样的,只是bbb.cpp中的struct X定义得改一下,因为有虚拟函数的类的头一个指针大小为指向虚拟函数表的指针。

     

    包含虚拟函数的aaa.cpp

    #include <stdio.h> // fprintf

     

    class X

    {

    public:

        virtual void xxx(); // 编码后的函数名和是否为虚拟函数无关

     

    private:

        int m;

        int n;

    };

     

    void X::xxx() // bbb.cpp完全以C函数方式调用类X的成员函数xxx

    {

        printf("m=%d, n=%d ", m, n);

    }

     

    bbb.cpp只需修改struct X的定义:

    struct X // 对应于aaa.cpp中类X

    {

        void* p; // 对应aaa.cpp中类X的虚拟函数表指针

        int m;

        int n;

    };

     

     

    其它操作步骤完全相同,运行同样可得到预期的结果。

     

  • 相关阅读:
    AntSword 中国蚁剑的下载安装配置(附下载文件)
    开园第一笔
    四舍五入小技巧
    PAT B# 1025 反转链表
    WebService如何根据对方提供的xml生成对象
    解决Web部署 svg/woff/woff2字体 404错误
    解决TryUpdateModel对象为空的问题
    IIS集成模式下,URL重写后获取不到Session值
    SQLServer清空数据库中所有的表并且ID自动归0
    win2003 64位系统IIS6.0 32位与64位间切换
  • 原文地址:https://www.cnblogs.com/aquester/p/10552138.html
Copyright © 2011-2022 走看看