我把c++ 代码贴出来吧,有什么错误欢迎指正
class St { public: St(int i) :_i(i) { } void Set(int i) { _i = i; } int Get() { return _i; } private: int _i; }; typedef void f(St* s); std::function<void(St*)> Set(int i) { return [&](St* s) { s->Set(i); }; } int main() { auto f = Set(100); St s(0); f(&s); std::cout << s.Get() << std::endl; return 0; }
![]() | 1 cmdOptionKana 2020-10-22 15:56:25 +08:00 不懂 C++,想问一下 C++有没有闭包的概念? 另外 Go 会自动初始化空值,比如 var ss St 之后,ss.Val 会自动初始化为 0,这里 C++ 是类似的规定吗? |
![]() | 2 kuro1 2020-10-22 15:56:55 +08:00 f 是个 function 啊 |
3 loriann OP c++ 没有闭包。ss.Val 是 0 我能理解,但是不太理解为什么 ss.Val 是 100 |
![]() | 4 ikw 2020-10-22 15:58:31 +08:00 贴代码可以用 markdown 语法,``` 或者 gist 这样的代码太难看了 另外,可以把 C++ 是随机数的代码也贴出来对比一下 |
![]() | 5 di1012 2020-10-22 15:59:30 +08:00 ss 的地址指到了 s |
![]() | 6 kuro1 2020-10-22 15:59:35 +08:00 给你换种写法,就是个赋值而已 ``` type St struct { Val int } func f1(s *St, v int) { s.Val = v } func TestA(t *testing.T) { var ss St f1(&ss, 100) fmt.Println(ss.Val) } ``` |
![]() | 7 ikw 2020-10-22 15:59:59 +08:00 |
![]() | 8 Orenoid 2020-10-22 16:00:13 +08:00 就通过传值设置进去的啊,你的疑问点是在于闭包? |
9 Mitt 2020-10-22 16:00:16 +08:00 via iPhone @loriann 因为闭包保存的是你闭包环境,你的闭包环境下 i 是 100,所以你执行它的时候设置的就是 100 |
![]() | 12 kuro1 2020-10-22 16:07:04 +08:00 https://tour.golang.org/moretypes/25 经典闭包 ``` package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i), ) } } ``` |
14 hdbzsgm 2020-10-22 16:14:43 +08:00 c++ capture list ??? |
![]() | 15 catror 2020-10-22 16:30:41 +08:00 via Android C++你把引用捕获改为赋值捕获就是 100 了 |
![]() | 17 lin07hui 2020-10-22 16:34:45 +08:00 Set(100) --> i = 100 --> f(&ss) --> s = &ss --> ss.Val = s.Val = i = 100 |
![]() | 18 akatquas 2020-10-22 16:57:14 +08:00 go 中关于闭包的一些实现过程可以看这个, [go 闭包的实现]( https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.6.html) 同时,把你的代码复制到,https://godbolt.org/ , 选择 go 语言,再选择 386gc tip,对照汇编结果来理解。 |
![]() | 21 zunceng 2020-10-22 17:10:49 +08:00 c++要把对象的作用域 生命周期在脑子里想的很清楚 我感觉这是最难地方 比语法规则难多了 |
22 loriann OP @catror 好吧,c++ capture 换成 = 号后我又有一个疑问,这个值是什么时候复制的,是在调 Set 的时候还是调 f 的时候??? |
![]() | 23 linjunyi22 2020-10-22 17:37:11 +08:00 闭包,Set 执行完后返回的是一个函数,再执行把指针传进去,那肯定把 ss 的值给改了 |
![]() | 24 catror 2020-10-22 18:04:00 +08:00 via Android @loriann 两步都有复制。调用 Set 的时候,复制了 100 到一个地方存下来。调用 f 的时候,再把存下来的值复制给 s._i |
![]() | 25 catror 2020-10-22 18:09:44 +08:00 via Android @loriann 在 lambda 表达式的内外,参数 i 的意义是不一样的。在表达式的内部:1. 引用捕获的时候,它是参数 i 的引用,调用 f 的时候,参数 i 已经没有了,所以最终得到的结果是随机的; 2. 赋值捕获的时候,它是参数 i 的复制 |
![]() | 26 catror 2020-10-22 18:19:22 +08:00 可能还是代码更直观点一点,Set 可以写成下面这样 ```cpp std::function<void(St*)> Set(int i) { // 赋值捕获 return [j=i](St* s) { s->Set(j); }; // // 引用捕获 // return [&j=i](St* s) { // s->Set(j); // }; } ``` |
27 loriann OP @catror 嗯,我有点明白了,执行 Set 的时候把 i 复制到了 j,执行 f 的时候函数引用的就是捕获的值 j,也就是 i 的一个复制 |
28 katsusan 2020-10-22 18:32:29 +08:00 Go 里的闭包函数在内存里是 code+data 形式表现的。 以你 Set 函数返回的 f 为例,在编译期就可以分析出它应该包含内部匿名函数的入口点地址和 i 的值, 在 x64 上就是 16 字节空间,也就是 main 栈上的 f 指向一个 16 字节的空间,执行 f=Set(100)的赋值后 第二个 8 字节就赋值为 100 。 执行 f(&ss)时参照 https://golang.org/s/go11func,在 x86_64 下 R0 用的是 DX 寄存器。 MOV …, R0 MOV 0(R0), R1 //R0+0=>R1,即函数入口地址 CALL R1 # called code can access “data” using R0,比如例子里的 100 就是 8(R0) |