我把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
zwpaper 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
zwpaper 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) |