先說結論: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; };
|
其它操作步驟完全相同,運行同樣可得到預期的結果。