go相关
更新日期:
这几天研究了一下go语言,顺便记录下一些重点或者感觉不太好理解的地方(与c++相比)。
1.array
数组的声明方式 var a [10]int
数组是多个相同类型数组的集合,不同于c++在go里array更像一个结构体或者vector,如果b=a这样赋值的话就直接把a的内容拷贝给了b,而且go中数组的长度是不可变的。
2.slice
slice的声明方式 var a []int或者b:=make([]int,3,10)或者c:=[]int{1,2,3}
与array声明方式的区别在于[]中有木有长度值
slice的长度是可变的,可以通过append增加元素。
slice更像一个结构体,它包含三个成员:pointer(指向数组) len(数组长度) cap(数组容量),所以当对slice进行互相赋值的时候也只是把这三个值给重置了而已,这样反倒更像C++中的数组了。
3.map chan
map chan好像c++中的指针,例如m:=map[int]string{1:”a”,2:”b”} m只是一个指针,它指向了一个map,如果把m赋值给另外一个map n,这时候m和n指向的地址相同,所以他们的成员也自然是一样的了。如果想把m的内容赋值给n需要遍历插入。chan也是一样的原理。
map里面的元素不可寻址,所以不可直接修改map元素的成员的值(map元素为指针则可修改),只可以修改元素值
- go里面所有的变量或者参数传递都是值传递,只是有些类型在底层实现上和指针类似才造成了引用传递的错觉(例如slice map chan)。(特别的,闭包里对外部变量的记录是引用)
- go里可以定义一个变量的指针,这里的指针具有C++里指针和引用的优点,它可以像指针一样多次赋值和传递,又可以像引用一样使用(操作成员不需要额外操作符)
4.struct
假设T是struct,那么Go里面遵循下面几个原则:
- T的方法集仅拥有 T Receiver 方法,即func (self T)xxx(){}
- *T 方法集则包含全部方法 (T + *T),即func (self T)xxx(){}和func (self *T)xxx(){}
- 如果结构的实例x是“可被寻址的”,且&x的方法集中包含方法m,则x.m()为(&x).m()的速记
5.interface
interface由类型和值组成,仅在类型和值为“nil”时才为“nil”。interface的类型和值会根据用于创建对应interface变量的类型和值的变化而变化。当你检查一个interface变量是否等于“nil”时,这就会导致未预期的行为。
interface{}可以存储任意值,同样是值传递,即传递的是对象实例则存储对象,传递的是对象指针则存储指针。当用switch inter.(type) case做判断时要注意对象和指针的类型区别。
通过interface引用的变量也是不可寻址的(同map的元素)1
2
3
4
5
6
7type myStruct struct {
text string
}
var in interface{} = &myStruct{"test"}
in.(*myStruct).text = "111" //正确
var in1 interface{} = myStruct{"test"}
in1.(myStruct).text = "111" //cannot assign to in.(myStruct).text
6.chan
chan和linux中pipe相似,可以用于多线程之间的通讯,一端读一端写,不用加锁。
chan有个问题,当向一个关闭的chan写入内容时会产生致命的错误(宕机),如果读取内容则会返回一个是否关闭的标志。_, ok := <-ch //ok==false 表示chan close
所以当你处理chan A时可以专门另外设置一个chan B来标识A的状态(这样可以避免从A中读数据来判断A),如1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21A:=make(chan int)
B:=make(chan int)
func IsClose() bool {
select {
case <-B:
return true
default:
return false
}
}
func Write(a int) bool {
select{
case <-B:
return false
case A<-a:
return true
}
}
func Close() {
close(B)
}