|
@@ -0,0 +1,95 @@
|
|
|
+# 介绍
|
|
|
+实现C面向对象写法:
|
|
|
+1. `pointer`分支是基于函数指针的封装;
|
|
|
+2. `opaque`分支是不透明结构体的封装。
|
|
|
+
|
|
|
+# 与不透明结构体的对比
|
|
|
+## 1.基于函数指针的封装:
|
|
|
+- 优点:
|
|
|
+ - 模拟面向对象编程,使用方便,调用简洁。
|
|
|
+ - 函数指针可以灵活替换,允许不同实例有不同的行为(类似于多态)。
|
|
|
+ - 代码更具可读性,通过对象访问函数使得代码显得更加面向对象。
|
|
|
+- 缺点:
|
|
|
+ - 增加了一定的复杂性,尤其是在初始化、管理函数指针和内存管理时。
|
|
|
+ - 效率上可能会有轻微的开销,因为每次调用都要通过函数指针间接调用实际函数。
|
|
|
+ - 函数指针可能导致调试变得更困难(比如在使用调试器时,无法直接看到函数调用链)。
|
|
|
+
|
|
|
+## 2.不透明结构体(Opaque Struct):
|
|
|
+- 优点:
|
|
|
+ - 更简单和直接,库的用户不需要关心结构体内部的实现细节,增加了封装性和代码的安全性。
|
|
|
+ - 函数调用时更加直观,通过全局函数操作对象,用户只看到需要操作的API接口。
|
|
|
+ - 减少了复杂性,尤其在内存管理和函数指针管理方面更为轻量。
|
|
|
+- 缺点:
|
|
|
+ - 不能模拟对象方法调用,调用方式可能显得不够“面向对象”。
|
|
|
+ - 不支持多态:所有实例共享相同的函数,不能为不同实例设置不同的行为。
|
|
|
+
|
|
|
+# 工程中的使用情况
|
|
|
+在实际工程中,**不透明结构体(Opaque Struct)** 的方式通常使用得更多。
|
|
|
+尤其是在系统编程、嵌入式开发或跨平台库设计中,不透明结构体是更常见的设计模式。
|
|
|
+它的封装性好,内存管理相对简单,并且函数调用更加高效。
|
|
|
+这种模式通常用于C库的设计,比如许多操作系统API和标准库都会采用这种模式。
|
|
|
+## 不透明结构体(Opaque Struct)的实际例子
|
|
|
+1. POSIX标准中的文件操作(FILE 结构体):
|
|
|
+`FILE` 结构体就是一个经典的不透明结构体。
|
|
|
+用户通过API函数如 `fopen()`, `fclose()`, `fread()`, `fwrite()` 操作 `FILE` 指针,但无法直接访问 `FILE` 结构体的内部实现。
|
|
|
+```c
|
|
|
+FILE* file = fopen("test.txt", "r");
|
|
|
+if (file) {
|
|
|
+ // 使用 fread、fwrite 等函数操作 FILE 对象
|
|
|
+ fclose(file);
|
|
|
+}
|
|
|
+```
|
|
|
+2. OpenGL 的 GLFW 库:
|
|
|
+`GLFW` 是一个图形库,它大量使用了不透明结构体的设计。用户只能通过API函数操作这些结构体,如 `GLFWwindow`。
|
|
|
+```c
|
|
|
+GLFWwindow* window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
|
|
|
+if (window) {
|
|
|
+ // 使用 glfwGetWindowSize 等函数操作 GLFWwindow
|
|
|
+ glfwDestroyWindow(window);
|
|
|
+}
|
|
|
+```
|
|
|
+3. libcurl
|
|
|
+`libcurl` 使用了不透明结构体,库的用户通过 `CURL`* 指针操作 HTTP 请求,而无法直接访问内部数据。
|
|
|
+```c
|
|
|
+CURL *curl = curl_easy_init();
|
|
|
+if(curl) {
|
|
|
+ curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
|
|
|
+ curl_easy_perform(curl);
|
|
|
+ curl_easy_cleanup(curl);
|
|
|
+}
|
|
|
+```
|
|
|
+## 基于函数指针封装的实际例子
|
|
|
+这种模式较少见于系统编程或者底层库设计,但在某些场景中,例如模拟面向对象编程、插件系统或者策略模式时,这种模式非常有用。
|
|
|
+1. 插件系统(Plugin System):
|
|
|
+在许多插件系统中,插件往往通过一个统一的结构体接口暴露函数指针。
|
|
|
+这种设计可以允许不同的插件使用不同的行为,但又通过统一的接口进行交互。
|
|
|
+例如,音频处理库 FFmpeg 中的一些模块设计就是通过函数指针进行交互。
|
|
|
+```c
|
|
|
+struct Plugin {
|
|
|
+ void (*init)(void);
|
|
|
+ void (*process)(void);
|
|
|
+ void (*destroy)(void);
|
|
|
+};
|
|
|
+```
|
|
|
+2. `SDL(Simple DirectMedia Layer)`事件处理:
|
|
|
+虽然SDL本身大多数时候使用不透明结构体,但在处理回调函数时,类似于函数指针的设计非常常见。
|
|
|
+事件驱动系统通过将函数指针传递给SDL,可以自定义事件的处理逻辑。
|
|
|
+```c
|
|
|
+SDL_Event event;
|
|
|
+while (SDL_PollEvent(&event)) {
|
|
|
+ switch (event.type) {
|
|
|
+ case SDL_QUIT:
|
|
|
+ quit();
|
|
|
+ break;
|
|
|
+ case SDL_KEYDOWN:
|
|
|
+ keydown(event.key.keysym.sym);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+# 总结
|
|
|
+- 不透明结构体(Opaque Struct) 是实际工程中更为常见的设计,特别是在需要跨平台和高效的API设计中。
|
|
|
+- 基于函数指针的封装 更适合在需要模拟面向对象行为,或需要为不同对象提供不同实现的场景。
|
|
|
+
|
|
|
+如果你的库需要高性能并且用户操作相对简单,`不透明结构体`是更合适的选择;而如果你希望提供面向对象的编程体验,可以考虑`基于函数指针的封装`。
|