Bug 1
在一个成员函数中对一个对象的成员变量进行赋值之后,在其他地方使用无效。
成员函数的说法存疑
v1
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package main
import "fmt"
type FileHandler struct { filename string } type Engine struct { fh *FileHandler }
func (engine Engine) init() { fh := &FileHandler{ filename: "a special filename", } engine.fh = fh fmt.Println(fmt.Sprintf("init: fh: %v", engine.fh)) } func (engine Engine) doSomething() { fmt.Println("doSomething", engine.fh) }
func main() { a := &Engine{} a.init() a.doSomething() }
|
输出
1 2
| init: fh: &{a special filename} doSomething <nil>
|
v2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main
import "fmt"
type FileHandler struct { filename string } type Engine struct { fh *FileHandler }
func (engine Engine) init() { fh := new(FileHandler) fh.filename = "new a special filename" engine.fh = fh fmt.Println(fmt.Sprintf("init: fh: %v", engine.fh)) } func (engine Engine) doSomething() { fmt.Println("doSomething", engine.fh) }
func main() { a := &Engine{} a.init() a.doSomething() }
|
输出
1 2
| init: fh: &{new a special filename} doSomething <nil>
|
v3
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package main
import "fmt"
type FileHandler struct { filename string } type Engine struct { fh *FileHandler }
func NewFileHandler() *FileHandler { return &FileHandler{ filename: "new a special filename", } }
func (engine Engine) init() { engine.fh = NewFileHandler() fmt.Println(fmt.Sprintf("init: fh: %v", engine.fh)) } func (engine Engine) doSomething() { fmt.Println("doSomething", engine.fh) }
func main() { a := &Engine{ fh: NewFileHandler(), } a.doSomething() }
|
输出
1
| doSomething &{new a special filename}
|
问题探讨
在v1和v2版本的代码中,在init函数中对engine的fh变量赋值之后,在其他的成员函数中进行使用发现指针仍为nil
。这完全颠覆了我的理解。(v1和v2的结果相同倒是可以理解,Go有逃逸机制)。只有v3这种写法才能让指针初始化成功。
应该对V1和V2进行如下改动,Engine
需要加指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package main
import "fmt"
type FileHandler struct { filename string } type Engine struct { fh *FileHandler val int }
func (engine *Engine) init() { fmt.Println(fmt.Sprintf("before init: val: %v", engine.val)) fh := &FileHandler{filename: "new a special filename"} engine.fh = fh engine.val = 100 fmt.Println(fmt.Sprintf("init: fh: %v", engine.fh)) } func (engine *Engine) doSomething() { fmt.Println("doSomething", engine.fh, " ", engine.val) }
func main() { a := Engine{val: -1} a.init() a.doSomething() fmt.Println(a.val) }
|
输出
1 2 3 4
| before init: val: -1 init: fh: &{new a special filename} doSomething &{new a special filename} 100 100
|
通过修正的写法,可以猜测到不添加指针的时候使用的是拷贝的一份engine执行函数操作,只有声明成指针的时候,拷贝的是对象的地址,执行函数操作才会改变原来对象的值,十分的interesting。
显然Go并不希望我们叫init和doSomething为成员函数。
好智障,果然奇怪的是我
补充一点,这里提到了new的用法,实际作用就是创建一个空间,初始化空间为0,返回空间的地址。本质上和&FileHandler{}
没有区别
刚遇到这个问题的时候,有一种三体里面的物理学家被智子困扰时的绝望,完全颠覆了我的理解。 果然我不是当科学家的料,只是个渣渣,想当然给写错了。