1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 12:32:21 +00:00

ch2-8-class: 增加 彻底解放C++的this指针

This commit is contained in:
chai2010 2018-05-04 07:46:46 +08:00
parent 7a55d875c4
commit 87012f4e51

View File

@ -401,3 +401,76 @@ struct Person {
我们在Person类中增加了一个叫New静态成员函数用于创建新的Person实例。在New函数中通过调用person_new来创建Person实例返回的是`person_handle_t`类型的id我们将其强制转型作为`Person*`类型指针返回。在其它的成员函数中我们通过将this指针再反向转型为`person_handle_t`类型然后通过C接口调用对应的函数。
到此我们就达到了将Go对象导出为C接口然后基于C接口再包装为C++对象以便于使用的目的。
## 彻底解放C++的this指针
熟悉Go语言的用法会发现Go语言中方法是绑定到类型的。比如我们基于int定义一个新的Int类型就可以有自己的方法
```go
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++中要实现类似的特性,一般会采用以下实现:
```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语言中的本质
```go
func (this Int) Twice() int
func Int_Twice(this Int) int
```
在Go语言中和this有着相似功能的类型接收者参数其实只是一个普通的函数参数我们可以自由选择值或指针类型。
如果以C语言的角度来思考this也只是一个普通的`void*`类型的指针我们可以随意自由地将this转换为其它类型。
```c++
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++到普通成员函数其实也是可以绑定到类型的。
只有纯虚方法是绑定到对象,那就是接口。