實戰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\n", 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\n", 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\n", so, dlerror());

        exit(1);

    }

 

    XXX xxx = (XXX)dlsym(h, argv[1]); // 取和類X的類成員函數xxx的函數地址,以便可以調用它

    if (NULL == xxx) {

        fprintf(stderr, "dlsym %s failed: %s\n", 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\n", m, n);

}

 

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

struct X // 對應於aaa.cpp中類X

{

    void* p; // 對應aaa.cpp中類X的虛擬函數表指針

    int m;

    int n;

};

 

 

其它操作步驟完全相同,運行同樣可得到預期的結果。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM