cgo不支持C++,须通过extern "C"封装为C接口:C++头文件用#ifdef __cplusplus extern "C"声明函数,实现文件正常写类,Go调用带C ABI的头文件并加#cgo CXXFLAGS: -std=c++11;内存与对象生命周期需Go显式管理,避免野指针和泄漏。

cgo 本身不支持 C++,必须通过 C 接口桥接
直接在 // #include 块里引用 .hpp 或写 class 会编译失败——cgo 的预处理器只按 C 语法解析,C++ 的 name mangling、模板、异常等特性它完全不识别。所有 C++ 逻辑必须先封装成纯 C 函数签名,再暴露给 Go。
常见错误现象:undefined reference to 'Foo::add(int, int)' 或 use of undeclared identifier 'extern',本质都是 C++ 符号没被降级为 C ABI。
- 在 C++ 头文件(如
compute.hpp)中,用#ifdef __cplusplus+extern "C"包裹函数声明,禁用 name mangling - C++ 实现文件(
compute.cpp)照常写类和方法,但只从 C 接口函数里调用它们 - Go 源文件顶部的
// #include "compute.h"必须指向那个带extern "C"的 C 头,不是原始 C++ 头 - 加
// #cgo CXXFLAGS: -std=c++11告诉 cgo 后续要调用 g++ 编译,否则默认用 gcc 会报error: 'std' does not name a type
内存传参时,C++ 对象生命周期必须由 Go 显式控制
不能让 C++ 构造的对象“自动”活在 Go goroutine 里——C++ 析构函数不会被 Go GC 触发,一旦对象在 C++ 层被销毁,后续 Go 调用 C.foo_method(ptr, x) 就是野指针读写。
典型场景:你需要在 Go 中创建、复用、最终释放一个 C++ 算法上下文(比如 FFT plan、矩阵分解器)。
立即学习“C++免费学习笔记(深入)”;
- 导出两个 C 函数:
void* create_algorithm();和void destroy_algorithm(void*);,分别调用new Foo()和delete static_cast<foo>(ptr)</foo> - Go 层用
unsafe.Pointer持有返回值,并在defer C.destroy_algorithm(ptr)保证释放 - 避免把
void*存进 map 或 channel 后丢失defer上下文;若需跨 goroutine 使用,确保所有调用完成后再destroy - 如果 C++ 方法返回的是栈上局部变量地址(如
const char* get_name() { return "foo"; }),可直接用;但若返回std::string.c_str()且该std::string是临时对象,就危险——必须在 C++ 层用strdup分配堆内存并由 Go 调用C.free
链接 C++ 静态库时,LDFLAGS 顺序和平台差异极易踩坑
Linux 下写 // #cgo LDFLAGS: -lfoo 默认找 libfoo.so,即使同目录有 libfoo.a 也不会用;macOS 更麻烦,archive 里未直接引用的符号会被 linker strip 掉,导致运行时报 symbol not found。
- 强制静态链接:Linux 写
// #cgo LDFLAGS: -Wl,-Bstatic -lfoo -Wl,-Bdynamic;macOS 必须加-Wl,-force_load,/path/to/libfoo.a - 路径优先于
-l:用完整路径更可靠,例如// #cgo LDFLAGS: /usr/local/lib/libfoo.a - 交叉编译时,静态库必须是目标平台 ABI 编译的(比如 darwin/arm64 的 Go build 不能链接 linux/amd64 的
.a),否则报file not recognized: file format not recognized - Windows 下注意
__declspec(dllimport),若 C++ DLL 导出函数没加这个修饰,链接时会提示undefined reference to 'xxx'
CString/GoString 与 C++ 字符串互转,别假设内存自动管理
C.CString 分配的是 C 堆内存,C.GoString 只是复制内容,两者都不触发对方的析构逻辑。C++ 里用 std::string 或 std::vector 返回的数据,Go 层绝不能直接当 C 字符串用。
- 输入字符串:Go 传
data := "input"→cstr := C.CString(data); defer C.free(unsafe.Pointer(cstr))→ 传给 C++ 函数 - 输出字符串:C++ 函数应返回
char*并保证内存长期有效(比如static std::string buf; buf = compute(); return const_cast<char>(buf.c_str());</char>),或分配新内存(strdup)并约定由 Go 调用C.free - 千万别写
C.GoString(C.CString("x"))—— 这是无意义的两次拷贝,且C.CString分配的内存没被C.free - 对二进制数据(如 float64 切片),用
unsafe.Slice((*C.double)(unsafe.Pointer(&data[0])), len(data))转为 C 数组指针,比用C.CString安全得多
最易被忽略的点:C++ 层所有 new/malloc 都必须配对 delete/free,而这些释放点必须明确暴露为 C 函数供 Go 调用;任何隐式依赖 RAII 或智能指针自动释放的逻辑,在 Go 侧都会失效。



















