C++ 项目约定:目录结构、依赖管理、构建与 Git

所谓「约定」= 一套项目结构规范 + 依赖管理规范:以后不管做什么项目,都按这个套路来,不用每次重新想。 适用背景:Windows(VS2022)只做 Boost + OpenCV,不做 Qt;Qt 开发放在 Linux(Ubuntu 虚拟机/服务器)。 文中 <OpenCV安装目录>、<Boost根目录>、<项目根目录> 为占位符,替换成本机实际目录即可。 一、总体原则 三句话: CMake 是唯一的构建系统,不碰 qmake、不碰手动 .vcxproj。 依赖一律走环境变量 + find_package,不在 CMakeLists.txt 里硬编码路径。 Windows 端 IDE 是 VS2022 + CMake 项目模式;Linux 端用 Qt Creator 或 VS Code。 二、目录结构约定(Windows 端) 每个项目固定长这样: <项目根目录>\<项目名>\ ├── CMakeLists.txt ├── CMakePresets.json ├── CMakeUserPresets.json # 本地预设(不提交 Git) ├── .gitignore ├── README.md ├── src\ # 源码 │ ├── main.cpp │ ├── core\ # Boost 业务逻辑 │ │ ├── Service.h │ │ └── Service.cpp │ └── vision\ # OpenCV 图像处理 │ ├── ImageProcessor.h │ └── ImageProcessor.cpp ├── include\ # 对外头文件(可选) ├── resources\ # 静态资源、图片(可选) ├── tests\ # 测试代码(可选) ├── third_party\ # 第三方源码(可选,一般不用) └── out\ # 构建输出(不提交 Git) └── build\ ├── debug\ └── release\ 关键点: ...

2026 年 10 月 13 日

channel 详解

C++ 里你得自己造「互斥锁 + 条件变量 + 队列」才能做到的线程通信,Go 用一个 channel 就把它变成了语言级原语 一、简述 channel(通道)是 Go 并发模型的核心,它的口号是: 「Do not communicate by sharing memory; instead, share memory by communicating.」 —— 不要通过共享内存来通信,而要通过通信来共享内存。 一个 channel 就是一个带类型的 FIFO 队列,goroutine 之间通过它发送 / 接收数据,同时天然完成了同步(谁等谁、什么时候等)与数据传递两件事。 作为 C++ 开发者,你过去写线程同步大概是这样:std::mutex 保护共享数据 + std::condition_variable 通知 + std::queue 存数据 + 各种 unique_lock。而 Go 把这三件套打包成了一个 channel:发送阻塞、接收阻塞、容量限制、关闭通知全部内建,而且它在语言层面保证了 channel 操作本身的线程安全。 本笔记覆盖:创建、无缓冲 / 有缓冲的阻塞语义、单向 channel、关闭与 range 消费、nil channel、死锁场景,以及「channel 当锁」的奇技淫巧。 二、C++ 对照速查表 概念 C++ Go 关键差异 消息队列 std::queue<T> + std::mutex + std::condition_variable ch := make(chan T) Go 一条语句搞定,且是语言级原语 无缓冲阻塞队列 手写「空/满都等条件变量」的队列 make(chan T)(默认无缓冲) 无缓冲 channel 天然同步:发=等收 有缓冲队列 std::queue + 信号量限制 make(chan T, 5) 缓冲即容量,满则发送阻塞 发送操作 queue.push(x); cv.notify_one(); ch <- x <- 方向右:发送 接收操作 queue.pop();(空时等 cv) x := <-ch <- 方向左:接收 队列空时接收 条件变量等待 + 谓词循环 阻塞直到有值 Go 不需要手写谓词循环 队列满时发送 信号量 / 容量检查 阻塞直到有空位 缓冲满才阻塞,缓冲空才阻塞 生命周期管理 队列对象手动析构、小心悬垂 close(ch),只能发送方关闭 关闭后 range 自动结束 多读多写 一把锁 + 一个队列满危险 内建线程安全,任意并发收发 Go 收发都允许并发,无需额外锁 非阻塞操作 try_lock / 检查队列非空 select + default(下篇讲) select 是非阻塞收发的标准姿势 三、逐主题详解 3.1 创建 channel:make package main import "fmt" func main() { // 无缓冲 channel:接收方和发送方必须同时准备好,相当于一次「握手」 ch1 := make(chan int) // 有缓冲 channel:容量为 5,缓冲不满时发送不阻塞 ch2 := make(chan string, 5) // 只声明不 make 的 channel 是 nil channel var ch3 chan float64 fmt.Println(ch3 == nil) // true(nil channel 不能收发,见 3.7) fmt.Println(ch1 == nil, ch2 == nil) // false, false _ = ch1 _ = ch2 } ⚠️ C++ 对照:make(chan int) 既有「分配」又有「初始化」两层含义,像 std::make_shared<T>()。注意这里返回的是 channel 本身,不是一个指针——但你如果 ch2 := make(chan int) 再 ch2 = ch2,传递的都是同一个底层通道的引用,多个 goroutine 共享它时不需要再加锁。 ...

2026 年 10 月 13 日

CLI 工具 flag 与 cobra

从 C++ 的 Boost.Program_options / CLI11 到 Go:标准库 flag 包入门,再上手 cobra 这个事实标准的命令行框架 一、简述 命令行工具(CLI)是 Go 最擅长的领域之一:编译出来是单一静态二进制,启动快、分发简单,天然适合写 git / docker 这类开发者工具。Go 在命令行的「外挂」上做了很多设计——os.Args 给你原始参数,标准库 flag 包给你简单的 -name value 解析,而 spf13/cobra(Kubernetes、Hugo、Docker CLI 大量使用)则提供子命令、flag 继承、自动帮助和 shell 补全的完整方案。 核心要点:C++ 里解析命令行要么手写循环扫 argv,要么拉 Boost.Program_options / CLI11;Go 的路径是「标准库 flag 够用即用,复杂命令行直接上 cobra」。cobra 的「命令树」思想对 C++ 开发者来说很像「用对象组合搭一棵命令路由树」。 二、C++ 对照速查表 概念 C++ Go 关键差异 入口参数 int main(int argc, char* argv[]) func main() + os.Args os.Args[0] 是程序名,Go 是切片可遍历 参数解析库 Boost.Program_options / CLI11 flag(标准库) flag 零依赖,但功能简单 子命令框架 自己搭 / CLI11 github.com/spf13/cobra cobra 是事实标准(k8s/gh/hugo 都用) 类型自动转换 boost::lexical_cast flag 绑定即转换 flag.Int 自动转,无需显式 cast 帮助文本 手动拼接打印 flag / cobra 自动生成 -h / --help 开箱即用 长短选项 -v / --verbose -v / --verbose 等价 flag 单双横线都认;布尔无需值 子命令树 无标准方案 cobra AddCommand 层层挂载 命令即对象,可嵌套 shell 补全 无标准方案 cobra completion 命令 生成 bash/zsh/fish 脚本 解析失败行为 抛异常 / 返回错误码 flag 默认 ExitOnError 可切换到 ContinueOnError 自己处理 三、逐主题详解 3.1 os.Args —— 原始参数切片 任何命令行的起点。和 C++ 的 argc/argv 相比,Go 把它做成了普通切片,不用数个数、不用指针算术: ...

2026 年 10 月 13 日

CMake 工程模板:VS2022 + Boost(静态库)

这是一个可直接复制的 Boost 工程模板(VS2022 + CMake + Ninja)。模板的作用:新建 Boost 项目时不用每次从零配置——复制模板、改项目名、加源文件即可。整套约定是:CMake 为唯一构建系统、依赖走环境变量 + find_package、构建输出在 out/build/、不写死路径。 前置环境变量: BOOST_ROOT = <Boost根目录> Boost 是静态库(vc143 构建),不用加 PATH,运行时不需要 dll 一、目录结构 <项目根目录>\BoostTemplate\ ├── CMakeLists.txt ├── CMakePresets.json ├── .gitignore ├── src\ │ └── main.cpp └── out\ └── build\ 二、CMakeLists.txt(复制到项目根目录) cmake_minimum_required(VERSION 3.20) project(BoostTemplate LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # ---------- Boost ---------- # BOOST_ROOT 由 CMakePresets.json 从环境变量注入,这里不写路径 set(Boost_NO_SYSTEM_PATHS ON) # 只从 BOOST_ROOT 找,不扫系统路径 set(Boost_USE_STATIC_LIBS ON) # 用静态库(vc143 静态构建) set(Boost_USE_MULTITHREADED ON) # 多线程版本 find_package(Boost 1.81 REQUIRED COMPONENTS filesystem system thread) # ---------- 源文件 ---------- set(SOURCES src/main.cpp ) add_executable(${PROJECT_NAME} ${SOURCES}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) target_link_libraries(${PROJECT_NAME} PRIVATE Boost::filesystem Boost::system Boost::thread ) 三、CMakePresets.json(复制到项目根目录) { "version": 3, "configurePresets": [ { "name": "debug", "generator": "Ninja", "binaryDir": "${sourceDir}/out/build/${presetName}", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "BOOST_ROOT": "$env{BOOST_ROOT}", "Boost_NO_SYSTEM_PATHS": "ON", "Boost_USE_STATIC_LIBS": "ON", "Boost_USE_MULTITHREADED": "ON" } }, { "name": "release", "inherits": "debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } } ], "buildPresets": [ { "name": "debug", "configurePreset": "debug" }, { "name": "release", "configurePreset": "release" } ] } 版本待核实:预设文件 "version": 3 与 CMakeLists.txt 里 cmake_minimum_required(VERSION 3.20) 的版本要求是否匹配(version 3 预设需较新的 CMake),首次配置前请确认本机 / VS2022 自带 CMake 的实际版本。 ...

2026 年 10 月 13 日

CMake 工程模板:VS2022 + OpenCV 4.10

这是一个可直接复制的 OpenCV 工程模板(VS2022 + CMake + Ninja)。模板的作用:新建 OpenCV 项目时不用每次手动加 include/lib 路径——复制模板、改项目名、加源文件即可。整套约定是:CMake 为唯一构建系统、依赖走环境变量 + find_package、构建输出在 out/build/、不写死路径。 前置环境变量: OpenCV_DIR = <OpenCV安装目录>\build\x64\vc16\lib PATH 追加:<OpenCV安装目录>\build\x64\vc16\bin 一、目录结构 <项目根目录>\OpenCVTemplate\ ├── CMakeLists.txt ├── CMakePresets.json ├── .gitignore ├── src\ │ └── main.cpp └── out\ └── build\ 二、CMakeLists.txt(复制到项目根目录) cmake_minimum_required(VERSION 3.20) project(OpenCVTemplate LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # ---------- OpenCV ---------- # OpenCV_DIR 由 CMakePresets.json 从环境变量注入,这里不写路径 find_package(OpenCV REQUIRED) # ---------- 源文件 ---------- set(SOURCES src/main.cpp ) add_executable(${PROJECT_NAME} ${SOURCES}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ${OpenCV_INCLUDE_DIRS} ) target_link_libraries(${PROJECT_NAME} PRIVATE ${OpenCV_LIBS} ) 三、CMakePresets.json(复制到项目根目录) { "version": 3, "configurePresets": [ { "name": "debug", "generator": "Ninja", "binaryDir": "${sourceDir}/out/build/${presetName}", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "OpenCV_DIR": "$env{OpenCV_DIR}" } }, { "name": "release", "inherits": "debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } } ], "buildPresets": [ { "name": "debug", "configurePreset": "debug" }, { "name": "release", "configurePreset": "release" } ] } 版本待核实:预设文件 "version": 3 与 CMakeLists.txt 里 cmake_minimum_required(VERSION 3.20) 的版本要求是否匹配(version 3 预设需较新的 CMake),首次配置前请确认本机 / VS2022 自带 CMake 的实际版本。 ...

2026 年 10 月 13 日

context 超时取消与传值

C++ 没有直接对应的东西——它最接近 std::jthread 的 stop_token:一个穿过调用链的「取消令牌 + 截止时间 + 键值袋」 一、简述 context.Context(标准库 context 包)是 Go 并发中传播取消信号、截止时间与请求级数据的载体。它贯穿整个标准库(HTTP 请求、数据库、RPC),是每一个「可能被取消的耗时代码」的标配第一参数。 它解决 C++ 里三个长期痛点: 传递取消信号:深调用链中,任何一层想取消整条链,怎么办?C++ 用 std::atomic<bool> 到处传 + 检查,或 std::jthread 的 stop_token。Go 里就是 ctx.Done() + 广播。 传递截止时间:每个函数都检查「现在是不是超时了」很烦。Go 里 context.WithDeadline 一路带过去,子任务自己查 ctx.Done()。 传递请求级数据:分布式的 trace id、用户信息等「横切数据」,Go 用 WithValue 携带,避免每个函数都加参。 核心要点:context.Context 只做三件事:取消(Cancel)、截止(Deadline/Timeout)、传值(Value)。 它不是「任意全局状态袋」,不该往里塞业务对象;也不是给每个函数都强加的仪式,而是给「可能被取消的耗时代码」用的。 二、C++ 对照速查表 概念 C++ Go 关键差异 取消令牌 std::stop_token / 手写 atomic<bool> ctx.Done() + 树形传播 Go 一次 cancel 广播整棵子树 超时取消 std::stoptoken + 手算时间 context.WithTimeout(ctx, 3*time.Second) 一行完成「到时自动 cancel」 截止时间 steady_clock::now() + d 自己算 context.WithDeadline(ctx, t) Go 语义是绝对时刻 手动取消 手写 atomic<bool> + 到处检查 cancelFunc()(WithCancel 返回) cancel 是「广播」,不只本层 关联请求数据 线程局部 / 全局 map(危险) context.WithValue(parent, key, val) Go 类型安全 + 随 ctx 走 goroutine 收尾 jthread.request_stop() + join select { case <-ctx.Done(): ... } Go 惯例:非主路径用 select 监听取消 取消原因 无 ctx.Err() → context.Canceled / DeadlineExceeded 区分「被取消」与「超时」 上下文传播 无(要么传 token,要么全局) 函数第一参数约定 Go 靠约定 + vet 检查不传 nil 三、逐主题详解 3.1 两个根:context.Background() 与 context.TODO() package main import ( "context" "fmt" ) func main() { // Background:程序的「根上下文」,所有 context 树的起点 ctx := context.Background() fmt.Println("Background Err =", ctx.Err()) // nil(永远不会被取消) fmt.Println("Background Done =", ctx.Done()) // nil channel(永不关闭) // TODO:还没想好用哪个 context 时的占位,语义同 Background todo := context.TODO() fmt.Println("TODO 等价于 Background:", todo.Err() == nil) _ = ctx _ = todo } 规则: ...

2026 年 10 月 13 日

dsh-image-analyze

DSH(DeepSeek Harness)插件:让纯文本模型也能"看图"。 图片、截图、PDF、Word 文档——统一路由到视觉模型(默认 mimo-v2.5)分析,对话模型只负责文字推理。全程同一供应商契约,按需付费。 English · 中文 ✨ 特性 能力 说明 🖼 四种图片入口 指定路径 / “分析最新截图”(免路径)/ 多张一次 / 桥接粘贴(配合 dsh-image-bridge) 📄 文档解析 PDF 逐页渲染(PyMuPDF)、Word(.docx) 解包内嵌图,一次送视觉模型分析 🎯 确定性视觉路由 视觉调用固定走同一供应商路由(opencode-go)+ 同一凭据,不引入第三方模型 💰 按需付费 只有真正看图时才产生视觉调用;mimo-v2.5 与主流文本模型同价 🔁 两种转述模式 relay(结构化视觉数据:OCR/布局/颜色/SVG)vs direct(视觉模型直接产出,零损耗) 🧹 空白截图自动跳过 小文件直跳 + PNG 像素方差检测,误触的空捕获不会浪费一次调用 📋 来源可见 每次分析返回"分析来源"清单,明确指出实际看了哪些文件 🔧 零依赖 插件本体纯 ESM、不 import 任何 @deepseek-ai 包,本地安装无需 npm registry;文档解析仅需系统 Python 🏗 架构与工作原理 ┌─ 图片入口 ─────────────────────────────────────────────┐ │ ① 指定路径 "分析 D:\xx\img.png" │ │ ② 最新截图 "分析最新截图 / 最新两张截图"(免路径) │ │ ③ 粘贴桥接 dsh-image-bridge 落盘 → [图片附件] 标记 │ │ ④ 文档 "分析 D:\xx\doc.pdf / report.docx" │ └────────────────────────┬───────────────────────────────┘ ▼ analyze_images / analyze_document 工具 │ ┌────────────────┼──────────────────┐ ▼ ▼ ▼ attachments ctx.llm.stream sources (持久化图片) provider: opencode-go (来源报告) model: mimo-v2.5 │ ▼ relay 模式 → 结构化视觉数据(OCR/布局/颜色/SVG)→ 对话模型推理 direct 模式 → 视觉模型直接产出最终答案(如 HTML 复刻) 设计要点 ...

2026 年 10 月 13 日

encoding/json 与配置文件

从 C++ 的 nlohmann/json 到 Go 的 encoding/json:结构体标签驱动的序列化 一、简述 Go 的 encoding/json 是标准库内置的 JSON 序列化/反序列化包。与 C++ 需要第三方库(nlohmann/json、rapidjson)不同,Go 通过**结构体字段标签(struct tag)**声明式地描述 JSON 映射关系,json.Marshal / json.Unmarshal 一键完成转换。 核心要点:Go 的 JSON 哲学是"结构体即 schema"——先用结构体定义数据形状,标签控制字段名、可选性、忽略规则。这也让配置文件读写(JSON/YAML)变得极其自然。 二、C++ 对照速查表 概念 C++ Go 关键差异 JSON 库 nlohmann/json 等第三方 encoding/json 标准库 开箱即用 对象映射 json j = json::parse(s) json.Unmarshal(data, &v) 需要目标结构体 序列化 j.dump() json.Marshal(v) 返回 []byte + error 字段改名 手动构造 struct tag `json:"name"` 声明式 忽略字段 手动排除 `json:"-"` 标签控制 可选字段 无 omitempty 零值不输出 动态 JSON json::object() / 数组 map[string]any / []any 用 any 容器 配置文件 Boost.PropertyTree / YAML 库 读 JSON 或第三方 YAML(gopkg.in/yaml.v3) JSON 为主 三、序列化与反序列化 3.1 基本用法 import ( "encoding/json" "fmt" ) type Person struct { Name string Age int } func main() { p := Person{Name: "Tom", Age: 30} // 序列化:结构体 → JSON data, err := json.Marshal(p) if err != nil { panic(err) } fmt.Println(string(data)) // {"Name":"Tom","Age":30} // 反序列化:JSON → 结构体 var p2 Person err = json.Unmarshal(data, &p2) if err != nil { panic(err) } fmt.Printf("%+v\n", p2) // {Name:Tom Age:30} } 3.2 结构体标签(关键) type Config struct { Server string `json:"server"` // 字段名映射为 server Port int `json:"port"` // 默认 0 Debug bool `json:"debug,omitempty"` // 零值时省略 Secret string `json:"-"` // 永远不参与 JSON Tags []string `json:"tags,omitempty"` // 空切片省略 } c := Config{Server: "localhost", Secret: "不输出"} data, _ := json.Marshal(c) fmt.Println(string(data)) // {"server":"localhost","port":0}(debug/tags 因 omitempty 省略,port 没有 omitempty 所以零值照样输出,secret 被 json:"-" 忽略) 标签规则: ...

2026 年 10 月 13 日

GMP 调度模型

C++ 里你要手写线程池、任务队列、负载均衡;Go 用 G/M/P 三件套在运行时层面把它全包了 一、简述 goroutine 不是 OS 线程,它比线程轻几个数量级。让几十万 goroutine 复用少数几个内核线程的,就是本章主角——GMP 调度模型。学完你会明白:go func() 背后发生了什么、为什么 goroutine 能在系统调用阻塞时"让位"、GOMAXPROCS 到底是什么、以及 M:N 用户态协程调度器是如何设计的。 核心要点:G(goroutine,任务)、M(Machine,OS 线程,工人)、P(Processor,调度上下文,工位)。P 的数量才是真正同时跑的 “线程数”,G 挂在 P 的本地队列上排队执行,M 抢到 P 才干活。运行时自动完成了 C++ 里你手写线程池 + 任务队列 + work-stealing 的全部工作。 二、C++ 对照速查表 概念 C++ Go 关键差异 并发执行单元 std::thread goroutine (G) std::thread 直接映射 OS 线程,G 是用户态任务 线程池 自写 / BS::thread_pool 等 内置 GMP Go 运行时自动复用 M,无需自建池 任务队列 std::queue<std::function> + mutex P 本地队列 runq 每个 P 有 256 长的本地队列,另有全局限队列 线程数量 自己定 / std::thread::hardware_concurrency GOMAXPROCS(默认 = CPU 核数) P 的数量决定并行度,M 可多于 P 线程阻塞迁移 无自动机制 M 阻塞时 P 移交 系统调用/锁阻塞时,P 被交给其它 M 继续执行 G 栈 固定大小(默认 1~8MB) 初始 2KB,动态增长 G 栈可扩容,按需分配 让出 CPU 无(靠 OS 时间片) runtime.Gosched() G 主动让出 P,避免饿死其它 G 抢占 内核时间片抢占 运行时信号抢占(Go 1.14+) 协作式 + 抢占式混合 work-stealing 手写 内置 空闲 P 偷其它 P 的队列任务,均衡负载 主线程 int main() m0 + g0 运行时专用线程和调度 goroutine 三、逐主题详解 3.1 三个字母分别是什么 ┌──────────────────────────────────────────────────────────────┐ │ GMP 关系示意 │ │ │ │ G (goroutine) M (machine) P (processor) │ │ ┌──────┐ ┌───────┐ ┌─────────────┐ │ │ │ 用户 │ │ 内核 │ │ 调度上下文 │ │ │ │ 代码 │──调度于──→ │ OS线程 │──绑定于──→│ 本地队列 │ │ │ │ 栈G │ │ 栈M │ │ runq[256] │ │ │ └──────┘ └───────┘ └─────────────┘ │ │ 任务 工人 工位 │ └──────────────────────────────────────────────────────────────┘ G(goroutine):一个待执行的函数 + 它的栈 + 上下文(寄存器现场)。创建的每个 go func() 都对应一个 G。它只是任务元数据,不是执行载体。G 结构体字段很长(src/runtime/runtime2.go),核心有栈描述 stack、当前函数 sched(保存 PC/SP)、状态 atomicstatus、所属 P 的指针、是否可被抢占等。 M(Machine):真实的 OS 线程,由内核调度。M 负责真正执行 G 的机器码。创建 M 要付出系统调用成本(约 1MB 内核栈 + 内核结构,线程栈),远贵于创建 G。M 通常不超过几千个,且有上限(受系统资源约束,默认 maxmcount=10000)。 P(Processor):你可以把它理解成"工位/许可令牌"。P 里有本地可运行队列 runq(长度 256)。G 想被调度运行,必须先被放进某个 P 的 runq,然后 P 绑定到某个 M 上执行队列里的 G。 P 的数量 = GOMAXPROCS(默认取 CPU 逻辑核数),它决定了同一时刻真正并行执行的 goroutine 上限。P 多了,并行度就高;P = 1 时行为接近单线程。 C++ 对照:M ≈ 一个真正运行在 CPU 上的线程;P ≈ 每个线程手头那个任务队列;G ≈ 任务队列里的任务项。你写线程池时是"一个线程,抢全局任务队列",Go 优化成"每线程一队列 + 偷取",减少锁竞争。 ...

2026 年 10 月 13 日

go mod 依赖管理

从 C++ 的 CMake+Conan/vcpkg 到 Go modules:官方集成、语义化版本与可复现构建 一、简述 Go 自 1.11 起引入 Go modules(常称 go mod)作为官方依赖管理工具,1.16 起默认启用。它把「依赖管理」内置到工具链里,一个 go.mod 文件声明模块与依赖,go.sum 锁定依赖的校验和,配合**语义化版本(v1.2.3)**和 GOPROXY 代理,实现可复现构建。 对于 C++ 开发者,可以把它理解为「CMake + Conan/vcpkg + 包清单 + 锁文件的四合一」:go.mod ≈ CMakeLists.txt + conanfile.txt,go.sum ≈ 锁文件,go get ≈ conan install,replace 指令 ≈ CMake 的本地路径引入。 核心要点:Go modules 不是「又一个包管理器」,而是语言官方、零配置文件的依赖解决方案。理解 go.mod 的四个指令(module / go / require / replace)就掌握了 90% 的日常操作。 二、C++ 对照速查表 概念 C++ Go 关键差异 构建系统 CMake / Makefile go build(内建) 无需写构建脚本 依赖清单 conanfile.txt / vcpkg.json go.mod 官方格式,随项目同时维护 依赖安装 conan install / vcpkg install go get / go mod tidy 不装到系统,进模块缓存 锁文件 Conan conan.lock(可选) go.sum 默认生成,锁定每行依赖版本+校验和 本地路径依赖 target_link_libraries 指向源码目录 replace ... => ../xxx 官方一等公民指令 语义化版本 Conan [requires] pkg/1.2.3 v1.2.3(标签含 v 前缀) Go 的版本就是 git tag 代理/镜像 自建 Artifactory / Nexus GOPROXY(如 goproxy.cn) 环境变量即可切换,无需配置仓库 私有仓库 CMake FetchContent + SSH GOPRIVATE + git 绕过代理直连 缓存目录 Conan 本地 cache $GOPATH/pkg/mod 全局共享,只读 vendor 机制 把第三方源码拷进仓库 go mod vendor 可选但常用,利于离线构建 三、逐主题详解 3.1 module 与 go.mod 三大字段 在项目根目录运行 go mod init 生成 go.mod: ...

2026 年 10 月 13 日