C 语言也可以是面向对象的
1、函数指针成员
指针也是一种变量类型,也可以当作结构体的成员,函数指针也不例外。比如我们定义 logger 结构体用于打印 log,其成员为一个函数指针,用于打印日志。
/// log.h #ifndef _LOG_H_ #define _LOG_H_ typedef struct logger logger; typedef void (*log_func)(logger* this, const char* msg); typedef struct logger { log_func log; } logger; void common_log_init(logger** args); void common_log_uninit(logger* args); #endif // _LOG_H_
/// log.c #include "log.h" #include <stdio.h> #include <stdlib.h> static void common_log(logger* this, const char* msg) { (void)this; printf("%s\n", msg); } void common_log_init(logger** args) { if (!args) { perror("args is nullptr"); return; } *args = (logger*)malloc(sizeof(logger)); if (!*args) { perror("out of memory"); return; } (*args)->log = common_log; } void common_log_uninit(logger* args) { free(args); }
/// main.c #include "log.h" int main() { logger* log; common_log_init(&log); log->log(log, "test msg"); common_log_uninit(log); return 0; }
经过编译后执行,
wuyong@Thinkpad:~/debug$ gcc log.c main.c -o log wuyong@Thinkpad:~/debug$ ./log test msg
2、继承与多态
C 语言结构体也可以继承,用于复用代码。比如我们希望在 logger 能够记录模块信息,则可以继承 logger 结构体,复用 log 函数指针,然后添加新的成员。
/// log.h ... void mylog_init(logger** args, const char* module); void mylog_uninit(logger* args); ...
/// mylog.c #include <stdio.h> #include <stdlib.h> #include "log.h" typedef struct mylog { logger base; #define log base.log const char* module; } mylog; static void my_log(logger* this, const char* msg) { mylog* _mylog = (mylog*)(this); printf("[%s] %s\n", _mylog->module, msg); } void mylog_init(logger** args, const char* module) { if (!args) { perror("args is nullptr"); return; } mylog* _mylog = (mylog*)malloc(sizeof(mylog)); if (!_mylog) { perror("out of memory"); return; } _mylog->log = my_log; _mylog->module = module; *args = (logger*)_mylog; } void mylog_uninit(logger* args) { free((mylog*)args); }
/// main.c #include "log.h" int main() { logger* log; mylog_init(&log, "main"); log->log(log, "test msg"); mylog_uninit(log); return 0; }
经过编译后执行
wuyong@Thinkpad:~/debug$ gcc main.c mylog.c -o log wuyong@Thinkpad:~/debug$ ./log [main] test msg
我们可以将所有函数指针放到 vtable 中
/// log.h ... typedef struct vtable { log_func log_info; log_func log_warn; log_func log_error; log_func log_fatal; } vtable; typedef struct logger { vtable log; } logger; ...
3、dlopen/dlsym 实现多态
Linux 提供了如下 API 来动态装载库
#include <dlfcn.h> void *dlopen(const char *filename, int flag); char *dlerror(void); void *dlsym(void *handle, const char *symbol); int dlclose(void *handle);
dlopen 打开一个动态链接库,并返回动态链接库的句柄。
- filename 是 so 库的加载路径,如果 filename 包含了路径符号 /,则 filename 被解释为相对或者绝对路径名,否则按照正常 so 搜索路径查查。
- flag 必须是下列两个值中的一个:RTLD_LAZY 和 RTLD_NOW。RTLD_LAZY 表示延迟绑定,只有在需要的时候解析符号(symbol);RTLD_NOW 表示在 dlopen 返回前,将 so 库中所有符号。
dlsym 根据 so 库的操作句柄和符号,返回符号对应的地址(不仅可以获取函数地址,也可以获取变量地址)。
使用 dlopen 和 dlsym 可以在运行时加载 so,使用不用的实现。
/// log.h #ifndef _LOG_H_ #define _LOG_H_ typedef struct logger logger; typedef struct op_callback op_callback; typedef void (*log_func)(logger* this, const char* msg); typedef int (*init_func)(logger** this, void* arg); typedef int (*uninit_func)(logger* this); typedef int (*entry_func)(op_callback* callback); typedef struct logger { log_func log_info; log_func log_warn; log_func log_error; log_func log_fatal; } logger; typedef struct op_callback { init_func init; uninit_func uninit; } op_callback; int log_entry(op_callback* callback); #endif // _LOG_H_
/// common_log.c #include <stdio.h> #include <stdlib.h> #include "log.h" static void log_info(logger* this, const char* msg) { (void)this; printf("[info] %s\n", msg); } static void log_warn(logger* this, const char* msg) { (void)this; printf("[warn] %s\n", msg); } static void log_error(logger* this, const char* msg) { (void)this; printf("[error] %s\n", msg); } static void log_fatal(logger* this, const char* msg) { (void)this; printf("[fatal] %s\n", msg); } static int logger_init(logger** this, void* arg) { (void)arg; if (!this) { perror("args is nullptr"); return -1; } *this = (logger*)malloc(sizeof(logger)); if (!*this) { perror("out of memory"); return -1; } (*this)->log_info = log_info; (*this)->log_warn = log_warn; (*this)->log_error = log_error; (*this)->log_fatal = log_fatal; return 0; } static int logger_uninit(logger* args) { free(args); return 0; } int log_entry(op_callback* callback) { if (!callback) { perror("callback is nullptr"); return -1; } callback->init = logger_init; callback->uninit = logger_uninit; return 0; }
/// mylog.c #include <stdio.h> #include <stdlib.h> #include "log.h" typedef struct mylog { logger base; #define log_info base.log_info #define log_warn base.log_warn #define log_error base.log_error #define log_fatal base.log_fatal const char* module; } mylog; static void log_info2(logger* this, const char* msg) { mylog* mylog_ = (mylog*)this; printf("[%s] [info] %s\n", mylog_->module, msg); } static void log_warn2(logger* this, const char* msg) { mylog* mylog_ = (mylog*)this; printf("[%s] [warn] %s\n", mylog_->module, msg); } static void log_error2(logger* this, const char* msg) { mylog* mylog_ = (mylog*)this; printf("[%s] [error] %s\n", mylog_->module, msg); } static void log_fatal2(logger* this, const char* msg) { mylog* mylog_ = (mylog*)this; printf("[%s] [fatal] %s\n", mylog_->module, msg); } int mylog_init(logger** this, void* arg) { if (!this) { perror("args is nullptr"); return -1; } mylog* _mylog = (mylog*)malloc(sizeof(mylog)); if (!_mylog) { perror("out of memory"); return -1; } _mylog->log_info = log_info2; _mylog->log_warn = log_warn2; _mylog->log_error = log_error2; _mylog->log_fatal = log_fatal2; _mylog->module = (const char*)arg; *this = (logger*)_mylog; return 0; } static int mylog_uninit(logger* args) { free(args); return 0; } int log_entry(op_callback* callback) { if (!callback) { perror("callback is nullptr"); return -1; } callback->init = mylog_init; callback->uninit = mylog_uninit; return 0; }
/// main.c #include <dlfcn.h> #include <stdio.h> #include <string.h> #include "log.h" static const char* mylog = "./libmylog.so"; static const char* commonlog = "./libcommonlog.so"; int main(int argc, const char* argv[]) { if (argc != 2) { printf("<%s> libmylog/libcommonlog\n", argv[0]); return -1; } const char* lib = NULL; void* arg = NULL; if (!strcmp(argv[1], "libmylog")) { lib = mylog; arg = "main"; } else if (!strcmp(argv[1], "libcommonlog")) { lib = commonlog; arg = NULL; } else { printf("not supported %s\n", argv[1]); return -1; } void* handle = dlopen(lib, RTLD_LAZY); if (!handle) { printf("failed to load %s, %s\n", lib, dlerror()); return -1; } entry_func entry = dlsym(handle, "log_entry"); if (!entry) { printf("failed to find log_entry, %s\n", dlerror()); return -1; } op_callback callback; if (entry(&callback)) { printf("faield to init callbakc\n"); return -1; } logger* log; callback.init(&log, arg); log->log_info(log, "test msg"); callback.uninit(log); return 0; }
编译后执行
wuyong@Thinkpad:~/debug$ gcc common_log.c -fPIC -shared -o libcommonlog.so wuyong@Thinkpad:~/debug$ gcc mylog.c -fPIC -shared -o libmylog.so wuyong@Thinkpad:~/debug$ gcc log.h main.c -o log -ldl wuyong@Thinkpad:~/debug$ ./log libcommonlog [info] test msg wuyong@Thinkpad:~/debug$ ./log libmylog [main] [info] test msg
欢迎关注微信公众号《源知源为》,更多技术分享。
#面向对象#C/C++基础 文章被收录于专栏
C/C++ 语言基础