Go语言高级编程

324课时
1.4K学过
8分

2. 第2章 CGO编程

引言

2.1 快速入门

2.1.1 最简CGO程序

2.1.2 基于C标准库函数输出字符串

2.1.3 使用自己的C函数

2.1.4 C代码的模块化

2.1.5 用Go重新实现C函数

2.1.6 面向C接口的Go编程

2.2 CGO基础

2.2.1 import "C"语句

2.2.2 #cgo语句

2.2.3 build tag 条件编译

2.3 类型转换

2.3.1 数值类型

2.3.2 Go 字符串和切片

2.3.3 结构体、联合、枚举类型

2.3.4 数组、字符串和切片

2.3.5 指针间的转换

2.3.6 数值和指针的转换

2.3.7 切片间的转换

2.4 函数调用

2.4.1 Go调用C函数

2.4.2 C函数的返回值

2.4.3 void函数的返回值

2.4.4 C调用Go导出函数

2.5 内部机制

2.5.1 CGO生成的中间文件

2.5.2 Go调用C函数

2.5.3 C调用Go函数

2.6 实战: 封装qsort

2.6.1 认识qsort函数

2.6.2 将qsort函数从Go包导出

2.6.3 改进:闭包函数作为比较函数

2.6.4 改进:消除用户对unsafe包的依赖

2.7 CGO内存模型

2.7.1 Go访问C内存

2.7.2 C临时访问传入的Go内存

2.7.3 C长期持有Go指针对象

2.7.4 导出C函数不能返回Go内存

2.8 C++ 类包装

2.8.1 C++ 类到 Go 语言对象

2.8.1.1 准备一个 C++ 类

2.8.1.2 用纯C函数接口封装 C++ 类

2.8.1.3 将纯C接口函数转为Go函数

2.8.1.4 包装为Go对象

2.8.2 Go 语言对象到 C++ 类

2.8.2.1 构造一个Go对象

2.8.2.2 导出C接口

2.8.2.3 封装C++对象

2.8.2.4 封装C++对象改进

2.8.3 彻底解放C++的this指针

2.9 静态库和动态库

2.9.1 使用C静态库

2.9.2 使用C动态库

2.9.3 导出C静态库

2.9.4 导出C动态库

2.9.5 导出非main包的函数

2.10 编译和链接参数

2.10.1 编译参数:CFLAGS/CPPFLAGS/CXXFLAGS

2.10.2 链接参数:LDFLAGS

2.10.3 pkg-config

2.10.4 go get 链

2.10.5 多个非main包中导出C函数

课程评价 (0)

请对课程作出评价:
0/300

学员评价

暂无精选评价
20分钟

2.8.3 彻底解放C++的this指针

熟悉Go语言的用法会发现Go语言中方法是绑定到类型的。比如我们基于int定义一个新的Int类型,就可以有自己的方法:

type Int int

func (p Int) Twice() int {
	return int(p)*2
}

func main() {
	var x = Int(42)
	fmt.Println(int(x))
	fmt.Println(x.Twice())
}

这样就可以在不改变原有数据底层内存结构的前提下,自由切换int和Int类型来使用变量。

而在C++中要实现类似的特性,一般会采用以下实现:

class Int {
	int v_;

	Int(v int) { this.v_ = v; }
	int Twice() const{ return this.v_*2; }
};

int main() {
	Int v(42);

	printf("%d\n", v); // error
	printf("%d\n", v.Twice());
}

新包装后的Int类虽然增加了Twice方法,但是失去了自由转回int类型的权利。这时候不仅连printf都无法输出Int本身的值,而且也失去了int类型运算的所有特性。这就是C++构造函数的邪恶之处:以失去原有的一切特性的代价换取class的施舍。

造成这个问题的根源是C++中this被固定为class的指针类型了。我们重新回顾下this在Go语言中的本质:

func (this Int) Twice() int
func Int_Twice(this Int) int

在Go语言中,和this有着相似功能的类型接收者参数其实只是一个普通的函数参数,我们可以自由选择值或指针类型。

如果以C语言的角度来思考,this也只是一个普通的void*类型的指针,我们可以随意自由地将this转换为其它类型。

struct Int {
	int Twice() {
		const int* p = (int*)(this);
		return (*p) * 2;
	}
};
int main() {
	int x = 42;
	printf("%d\n", x);
	printf("%d\n", ((Int*)(&x))->Twice());
	return 0;
}

这样我们就可以通过将int类型指针强制转为Int类型指针,代替通过默认的构造函数后new来构造Int对象。 在Twice函数的内部,以相反的操作将this指针转回int类型的指针,就可以解析出原有的int类型的值了。 这时候Int类型只是编译时的一个壳子,并不会在运行时占用额外的空间。

因此C++的方法其实也可以用于普通非 class 类型,C++到普通成员函数其实也是可以绑定到类型的。 只有纯虚方法是绑定到对象,那就是接口。