You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何分离库的API与实现?共享库编译遇未解析符号问题

问题分析与解决方案

错误原因解析

当BUILD_SHARED_LIBS=ON时,example作为动态库,GenerateExportHeader生成的EXAMPLE_EXPORT宏,在未定义EXAMPLE_STATIC且未链接example的example-impl中会被解析为__declspec(dllimport)。这意味着example-impl会尝试从外部动态库导入Example的构造、析构函数等符号,但此时example-impl未链接example,导致链接器找不到这些符号。

静态库模式下无问题的原因是:静态库是目标文件的归档,最终链接可执行文件时,链接器会从两个静态库中提取所需符号并合并,无需提前显式链接。


解决方案一:明确链接依赖(合理且必要)

在共享库场景下,example-impl继承自Example,必须能访问基类的符号,显式链接是正确且必要的操作。

修改src/CMakeLists.txt,给example-impl添加对example的链接依赖:

add_library(example-impl example_impl.cpp example_impl.h)
# 添加链接依赖
target_link_libraries(example-impl PRIVATE example)
target_include_directories(
    example-impl
    PRIVATE
    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>"
)
target_compile_features(example-impl PRIVATE cxx_std_17)

此修改适配两种构建模式:

  • 静态库模式:符号会被合并到最终目标文件
  • 共享库模式:example-impl会链接example动态库,正确导入基类符号

解决方案二:纯接口+工厂模式(彻底分离API与实现)

如果希望API库完全不包含任何实现代码,仅作为纯接口存在,可以采用这种模式:

1. 重构API为纯接口

修改include/example/example.h,让Example成为纯虚接口,并添加工厂函数声明(删除src/example.cpp):

#pragma once

#include <example/export.h>

namespace EX {
    class EXAMPLE_EXPORT Example {
      public:
        virtual ~Example() = default;
        // 将原非虚函数改为纯虚,公共逻辑移至实现类或工具类
        virtual void func1() = 0;
        virtual void func2() = 0;
    };

    // 工厂函数,由实现库提供具体实例
    EXAMPLE_EXPORT Example* create_example();
}

2. 调整CMake配置

将API库改为接口库(仅提供头文件):

# 纯接口库,无实现代码
add_library(example INTERFACE)
target_include_directories(
    example
    INTERFACE
    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
)
target_compile_features(example INTERFACE cxx_std_17)

include(GenerateExportHeader)
generate_export_header(
    example
    EXPORT_MACRO_NAME EXAMPLE_EXPORT
    EXPORT_FILE_NAME "${PROJECT_SOURCE_DIR}/include/example/export.h"
    STATIC_DEFINE EXAMPLE_STATIC
)

add_library(example-impl example_impl.cpp example_impl.h)
# 链接接口库
target_link_libraries(example-impl PRIVATE example)
target_include_directories(
    example-impl
    PRIVATE
    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>"
)
target_compile_features(example-impl PRIVATE cxx_std_17)

3. 实现工厂函数与接口逻辑

在src/example_impl.cpp中补充实现:

#include "example_impl.h"

namespace EX {
    ExampleImpl::ExampleImpl() {}
    ExampleImpl::~ExampleImpl() {}
    void ExampleImpl::func1() { /* 实现原公共逻辑 */ }
    void ExampleImpl::func2() { /* 实现特定逻辑 */ }

    Example* create_example() {
        return new ExampleImpl();
    }
}

这种模式的优势:

  • API库example完全无实现代码,仅定义接口规范
  • 实现库example-impl负责所有逻辑实现
  • 用户只需链接example-impl即可获取功能,无需关注API库的细节
  • 天然适配静态/共享库两种模式,无链接符号问题

内容的提问来源于stack exchange,提问作者RafalMaziejuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 10:08:15