用以定义一个自定义的集合类数据结构,配合 interface 实现面向对象的设计。不像c++、java,有显式的构造和析构函数,在go语言中,没有构造函数,析构的话则是提供了SetFinalizer进行变相支持。
SetFinalizer
终止器只有在对象被 GC 时,才会被执行。其他情况下,都不会被执行,即使程序正常结束或者发生错误。
定义:
func SetFinalizer(x, f interface{})
参数说明如下:
- 参数 x 必须是一个指向通过 new 申请的对象的指针,或者通过对复合字面值取址得到的指针。
- 参数 f 必须是一个函数,它接受单个可以直接用 x 类型值赋值的参数,也可以有任意个被忽略的返回值。
SetFinalizer 函数可以将 x 的终止器设置为 f,当垃圾收集器发现 x 不能再直接或间接访问时,它会清理 x 并调用 f(x)。另外,x 的终止器会在 x 不能直接或间接访问后的任意时间被调用执行,不保证终止器会在程序退出前执行,因此一般终止器只用于在长期运行的程序中释放关联到某对象的非内存资源。例如,当一个程序丢弃一个 os.File 对象时没有调用其 Close 方法,该 os.File 对象可以使用终止器去关闭对应的操作系统文件描述符。
顺序:如果 A 指向 B,两者都有终止器,且 A 和 B 没有其它关联,那么只有 A 的终止器执行完成,并且 A 被释放后,B 的终止器才可以执行。如果 *x 的大小为 0 字节,也不保证终止器会执行。清理:
我们也可以使用SetFinalizer(x, nil)来清理绑定到 x 上的终止器。
package main import ( "fmt" "runtime" ) type Test1 struct { b int } type Test2 struct { t *Test1 a int } func test() { t1 := Test1{ b: 10, } runtime.SetFinalizer( &t1, func(t1 *Test1) { fmt.Println("test1") }, ) t2 := Test2{ t: &t1, a: 20, } runtime.SetFinalizer( &t2, func(t2 *Test2) { fmt.Println("test2") }, ) } func main() { test() for i := 0; i < 10; i++ { runtime.GC() } for { } }
会根据引用关系 test2->test1,顺序调用 test2 test2。为何?
如果先调用test1那么这个内存就失效了,如果在test2中再进行析构,就会面临一个无效的地址,从语法都层面无法保证执行的正确性,所以一定是按照引用顺序,顺序的析构。
类型内嵌
结构体可以包含一个或多个匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字。匿名字段本身可以是一个结构体类型,即结构体可以包含内嵌结构体。注意,一种类型只能有一个内嵌,不能有多个可以粗略地将这个和面向对象语言中的继承概念相比较,随后将会看到它被用来模拟类似继承的行为。Go语言中的继承是通过内嵌或组合来实现的,所以可以说,在Go语言中,相比较于继承,组合更受青睐。
type Test1 struct { b int } type Test2 struct { Test1 int a int }
下面代码输出什么?
package main import ( "fmt" ) type testInterface interface { name() } type Test1 struct { } func (t *Test1) hello() { t.name() } func (t *Test1) name() { fmt.Println("Test1") } type Test2 struct { Test1 } func (t *Test2) name() { fmt.Println("Test2") } func main() { t := Test2{} t.hello() }
Test1
此处可以从符号表、作用域来看,首先 Test2中只包含了name这一个func。其次t.hello()等价于t.Test1.hello(),查找的本质是Test1的符号表,Test1包含hello、name,这里各自的name是各自的,并且也只能看到自己的。所以最终输出的是Test1。
结构体的相等如何判断?
package main import "fmt" func main() { sn1 := struct { age int name string }{age: 11, name: "qq"} sn2 := struct { age int name string }{age: 11, name: "qq"} if sn1 == sn2 { fmt.Println("sn1 == sn2") } sm1 := struct { age int m map[string]string }{age: 11, m: map[string]string{"a": "1"}} sm2 := struct { age int m map[string]string }{age: 11, m: map[string]string{"a": "1"}} if sm1 == sm2 { fmt.Println("sm1 == sm2") } }
./test2.go:31:9: invalid operation: sm1 == sm2 (struct containing map[string]string cannot be compared)
那么这里可以得到结构体的比较规则:
- 结构体比较规则注意1:只有相同类型的结构体才可以比较,结构体是否相同不但与属性类型个数有关,还与属性顺序相关.比如:
sn1 := struct { age int name string }{age: 11, name: "qq"} sn3:= struct { name string age int }{age:11, name:"qq"}
sn3与sn1就不是相同的结构体了,不能比较。
- 结构体比较规则注意2:结构体是相同的,但是结构体属性中有不可以比较的类型,如map,slice,则结构体不能用==比较。
可以使用reflect.DeepEqual进行比较
if reflect.DeepEqual(sm1, sm2) { fmt.Println("sm1 == sm2") } else { fmt.Println("sm1 != sm2") }
补充一个例子,也是类似的代码,以供参考
package main import ( "fmt" "reflect" ) type Student1 struct { Name string Address string } type Student2 struct { Name string Address string } type People1 struct { Name string Score map[string]int } type People2 struct { Name string Score map[string]int } func main() { s1 := Student1{ Name: "bob", Address: "china", } s2 := Student2{ Name: "bob", Address: "china", } //if s1 == s2 { // 类型不一致,不能比较 // fmt.Println("相等") //} if reflect.DeepEqual(s1, s2) { // 类型不一样,也就不相等 fmt.Println("s1 == s2") } else { fmt.Println("s1 != s2") } s3 := struct { Name string Address string }{ Name: "bob", Address: "china", } s4 := struct { Name string Address string }{ Name: "bob", Address: "china", } if s3 == s4 { // 匿名struct,相当于都是一个类型,这个类型由其内部的成员属性决定,包含:变量名称、变量类型、变量顺序 fmt.Println("s3 == s4") } else { fmt.Println("s3 != s4") } p1 := People1{ Name: "Bob", Score: map[string]int{ "math": 90, }, } p2 := People1{ Name: "Bob", Score: map[string]int{ "math": 80, }, } if reflect.DeepEqual(p1, p2) { fmt.Println("p1 == p2") } else { fmt.Println("p1 != p2") } }
➜ spec git:(master) ✗ go build -o main c_struct.go ➜ spec git:(master) ✗ ./main s1 != s2 s3 == s4 p1 != p2#golang工程师#