实现c api的demo

lujw2 fefc4652a2 docs: add README.md 11 months ago
include a7ef917033 feat: add OOP polymorphism to image library 10 months ago
src a7ef917033 feat: add OOP polymorphism to image library 10 months ago
test a7ef917033 feat: add OOP polymorphism to image library 10 months ago
.gitignore a7ef917033 feat: add OOP polymorphism to image library 10 months ago
CMakeLists.txt a7ef917033 feat: add OOP polymorphism to image library 10 months ago
README.md fefc4652a2 docs: add README.md 10 months ago

README.md

介绍

实现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 结构体的内部实现。

    FILE* file = fopen("test.txt", "r");
    if (file) {
    // 使用 fread、fwrite 等函数操作 FILE 对象
    fclose(file);
    }
    
  2. OpenGL 的 GLFW 库: GLFW 是一个图形库,它大量使用了不透明结构体的设计。用户只能通过API函数操作这些结构体,如 GLFWwindow

    GLFWwindow* window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (window) {
    // 使用 glfwGetWindowSize 等函数操作 GLFWwindow
    glfwDestroyWindow(window);
    }
    
  3. libcurl libcurl 使用了不透明结构体,库的用户通过 CURL* 指针操作 HTTP 请求,而无法直接访问内部数据。

    CURL *curl = curl_easy_init();
    if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
    curl_easy_perform(curl);
    curl_easy_cleanup(curl);
    }
    

    基于函数指针封装的实际例子

    这种模式较少见于系统编程或者底层库设计,但在某些场景中,例如模拟面向对象编程、插件系统或者策略模式时,这种模式非常有用。

  4. 插件系统(Plugin System): 在许多插件系统中,插件往往通过一个统一的结构体接口暴露函数指针。 这种设计可以允许不同的插件使用不同的行为,但又通过统一的接口进行交互。 例如,音频处理库 FFmpeg 中的一些模块设计就是通过函数指针进行交互。

    struct Plugin {
    void (*init)(void);
    void (*process)(void);
    void (*destroy)(void);
    };
    
  5. SDL(Simple DirectMedia Layer)事件处理: 虽然SDL本身大多数时候使用不透明结构体,但在处理回调函数时,类似于函数指针的设计非常常见。 事件驱动系统通过将函数指针传递给SDL,可以自定义事件的处理逻辑。

    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设计中。
  • 基于函数指针的封装 更适合在需要模拟面向对象行为,或需要为不同对象提供不同实现的场景。

如果你的库需要高性能并且用户操作相对简单,不透明结构体是更合适的选择;而如果你希望提供面向对象的编程体验,可以考虑基于函数指针的封装