target_include_directories对比 PUBLIC / PRIVATE

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


通过实操对比 PUBLIC / PRIVATE 的效果

一、表述详解

bash 复制代码
cmake_demo/
├── include/
│   └── lib.h      # 带导出宏的头文件
├── src/
│   ├── lib.cpp    # 动态库实现
│   └── main.cpp   # 调用方程序
└── CMakeLists.txt

核心结论

target_include_directories(mylib PRIVATE 路径) 的适用场景:
该头文件路径仅被 lib.cpp(库的实现文件)使用,不会被 main.cpp(依赖该库的外部代码)包含 ,此时用 PRIVATE

如果外部代码(main.cpp)需要包含这个路径下的头文件(lib.h),必须用 PUBLIC,否则外部目标会找不到头文件。


补充核心定义(关键)

作用域 适用场景
PUBLIC 路径既给 lib.cpp 用,也给所有依赖 mylib 的目标(如main.cpp)用(接口头文件路径)
PRIVATE 路径仅给 lib.cpp 自用,不传递给外部依赖目标(库内部私有头文件路径)
INTERFACE 路径不给自身用,仅传递给外部依赖目标

二、完整项目示例

1. 项目目录结构

采用标准工程规范,公共头文件放在 include 目录,源文件放在 src 目录:

复制代码
cmake_demo/
├── include/       # 公共头文件目录(外部可访问)
│   └── lib.h      # 库接口声明
├── src/
│   ├── lib.cpp    # 库实现文件
│   └── main.cpp   # 主程序(调用库函数)
└── CMakeLists.txt # 构建脚本

2. 源码文件

include/lib.h(库的公共接口)

头文件保护宏防止重复包含,声明加法函数,会被 main.cpp 直接包含:

c 复制代码
#ifndef LIB_H
#define LIB_H

// 加法函数声明:对外提供的接口
int add(int a, int b);

#endif // LIB_H

src/lib.cpp(库的实现文件)

包含公共头文件,实现加法逻辑:

c 复制代码
// 包含库的接口头文件
#include "lib.h"

// 实现加法函数
int add(int a, int b) {
    return a + b;
}

src/main.cpp(主程序)

调用库的 add 函数,需要包含 lib.h

c 复制代码
#include <stdio.h>
// 包含库的公共头文件
#include "lib.h"

int main() {
    int res = add(10, 20);
    printf("10 + 20 = %d\n", res);
    return 0;
}

3. CMake 构建脚本

写法1:使用 PUBLIC(标准正确用法)

因为 main.cpp 需要依赖 include 路径找到 lib.h,因此路径需要传递给外部目标 ,用 PUBLIC

cmake 复制代码
cmake_minimum_required(VERSION 3.15)
project(AddLibraryDemo)

# C++标准配置
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 1. 创建静态库目标 mylib
add_library(mylib src/lib.cpp)

# 2. 为库添加头文件路径:PUBLIC 表示路径自身可用,且传递给依赖它的目标
target_include_directories(mylib PUBLIC include)

# 3. 创建可执行目标
add_executable(main_app src/main.cpp)

# 4. 链接库:main_app 会自动继承 mylib 的 PUBLIC/INTERFACE 头文件路径
target_link_libraries(main_app PRIVATE mylib)

写法2:错误演示(改用 PRIVATE

如果我们把 PUBLIC 改为 PRIVATE,代表 include 路径仅给 mylib 自身使用,不传递

cmake 复制代码
# 仅修改这一行,其余代码不变
target_include_directories(mylib PRIVATE include)

4. 编译与效果测试

编译命令(终端执行)

bash 复制代码
# 创建构建目录并进入
mkdir build && cd build
# 生成构建文件
cmake ..
# 编译
make

结果对比

  1. PUBLIC 配置 :编译成功,运行 ./main_app,输出:

    复制代码
    10 + 20 = 30
  2. PRIVATE 配置编译直接报错 ,提示找不到头文件:

    复制代码
    fatal error: lib.h: No such file or directory
    #include "lib.h"

    原因:main_app 无法继承 include 路径,编译器找不到头文件。


三、PRIVATE正确使用场景

为了让你理解 PRIVATE 不是无用的,我们扩展场景:给库添加私有内部头文件 ,仅 lib.cpp 使用,外部无需访问。

调整后的目录结构

复制代码
cmake_demo/
├── include/               # 公共头文件(对外)
│   └── lib.h
├── src/
│   ├── private_inc/       # 库私有头文件(仅内部用)
│   │   └── internal.h
│   ├── lib.cpp
│   └── main.cpp
└── CMakeLists.txt

新增文件:src/private_inc/internal.h

c 复制代码
#ifndef INTERNAL_H
#define INTERNAL_H
// 库内部使用的工具函数,不对外部暴露
static int calc(int x) {
    return x * 2;
}
#endif

修改 src/lib.cpp

c 复制代码
#include "lib.h"
// 包含私有内部头文件
#include "internal.h"

int add(int a, int b) {
    // 调用内部工具函数
    return calc(a + b);
}

优化后的 CMake 配置

公共路径用 PUBLIC,私有路径用 PRIVATE

cmake 复制代码
cmake_minimum_required(VERSION 3.15)
project(AddLibraryDemo)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(mylib src/lib.cpp)

# 公共头文件:传递给外部依赖目标
target_include_directories(mylib PUBLIC include)
# 私有头文件:仅库自身使用,不传递
target_include_directories(mylib PRIVATE src/private_inc)

add_executable(main_app src/main.cpp)
target_link_libraries(main_app PRIVATE mylib)

效果

  1. main_app 能继承 include 路径,正常找到 lib.h
  2. main_app 无法访问 src/private_inc 路径,保证了库内部实现的封装性;
  3. lib.cpp 可以同时找到公共头文件和私有头文件。

四、总结

  1. 表述纠正main.cpp 依赖该头文件路径时,必须用 PUBLIC;仅 lib.cpp 依赖时,才用 PRIVATE
  2. 核心规则
    • 对外暴露的接口头文件路径 → PUBLIC(传递给依赖目标);
    • 库内部私有的实现头文件路径 → PRIVATE(仅自身使用,封装隔离);
  3. 单目标/多目标通用 :这套规则是现代CMake的标准规范,无论项目大小都推荐使用,比全局的 include_directories 更安全、更易维护。
相关推荐
Titan20242 小时前
搜索二叉树笔记模拟实现
数据结构·c++·笔记·学习
LYOBOYI1232 小时前
qml的布局策略
c++·qt
sycmancia2 小时前
C++进阶02——C++和C中const的区别、三目运算符、引用的本质
c++
牙牙要健康2 小时前
【open3d】Windows 下编译 Open3D C++ 源码完整教程
开发语言·c++·windows
不染尘.2 小时前
二叉树相关题目
开发语言·数据结构·c++·算法
maplewen.2 小时前
C++11 std::mutex
开发语言·c++
历程里程碑2 小时前
21:重谈重定义理解一切皆“文件“及缓存区
linux·c语言·开发语言·数据结构·c++·算法·缓存
wxin_VXbishe2 小时前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·spring boot·python·spring·django·php
oldmao_20002 小时前
第五章 C++内存模型与原子操作
c++