经常会有一些朋友问go语言的一些问题和疑惑,其实好多问题在官方文档和*里都有详细的讲解,只要你肯花时间读一遍官方文档和Effective Go基本上都有找到答案。本文总结一下大家经常问到的一些问题,长期更新。
代码都在github上, 地址 https://github.com/lpxxn/gocommonquestions
new 和make 的区别
简单来说,new(T)用于分配内存,返回指向T类型的一个指针,指针的值为T类型的零值
n1 := new(int)
fmt.Println(n1) // console the address of n1
fmt.Println(*n1 == ) // zero value type T struct {
I int
A string
Next *T
}
n2 := new(T)
fmt.Println(n2)
fmt.Println(n2.I == )
fmt.Println(n2.Next == nil)
fmt.Println(n2.A == "") n3 := new([]int)
fmt.Println(n3)
fmt.Println(*n3 == nil)
make(T) 只能用于slice、map和channel, 返回非零值的T。
m1 := make([]int, )
fmt.Println(m1)
m2 := make(map[int]string)
m2[] = "abcde"
m3 := make(chan int) m4 := make(chan int, )
make 返回的是类型本身,new 返回的是指向类型的指针
相关讲解
https://*.com/questions/9320862/why-would-i-make-or-new
https://golang.org/doc/effective_go.html#allocation_new
https://golang.org/ref/spec#The_zero_value
是否需要主动关闭channel
除非你的程序需要等待channel关闭后做一些操作,不用主动去关闭channel。当没有地方在使用这个channel的时候go的垃圾回收系统会自动回收,如果channel里还有值,但是没有地方引用了,也会被回收。
下面的小例子就是在等待channel c1和c2关闭后,再做一些事情。
func main() {
c1 := make(chan int, )
go test1(c1)
for v := range c1 {
fmt.Println(v)
} fmt.Println("after close c1 do something")
c2 := make(chan bool)
go func() {
time.AfterFunc(time.Second * , func() {
close(c2)
})
}()
_, close := <- c2
if !close {
fmt.Println("after c2 closed do something")
} fmt.Println("end")
} func test1(c chan<- int) {
for i := ; i < ; i++ {
c <- i
}
close(c)
}
简单测试垃圾回收channel
func createChan() chan int64{
c := make(chan int64, )
c <-
return c
} func main() { RM()
for i := ; i < ; i++ {
c := createChan()
c <-
RM()
runtime.GC()
}
RM()
} func RM(){
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Println(m.Alloc)
}
相关讲解:https://*.com/questions/8593645/is-it-ok-to-leave-a-channel-open
https://groups.google.com/forum/#!msg/golang-nuts/pZwdYRGxCIk/qpbHxRRPJdUJ
https://groups.google.com/forum/#!topic/golang-nuts/KtIyc5lTRJY
Unbuffered channel和buffered channel 区别
buffered channel
c3 := make(chan bool, ) // buffered channel
buffered channel 可以持续的发送数据到channel,直到channel满为至。不用等待是否有接收channel。
如果channel满了,会等待读取channel,当有channel被读取,就会继续发送数据到channel
c3 := make(chan bool, ) // buffered channel go func() {
for i := ; i < ; i++ {
c3 <- i % ==
}
close(c3)
}() for v := range c3 {
fmt.Println(v)
}
unbuffered channel
下面这两种声明是一样的
c1 := make(chan bool, ) // unbuffered channel
c2 := make(chan bool) // unbuffered channel
unbuffered channel 的接收channel会一直阻塞,直到有值传给channel, 也可以说发送channel会一直阻塞,至到有接收channel
c1 := make(chan bool, ) // unbuffered channel
c2 := make(chan bool) // unbuffered channel go func() {
c1 <- false
time.Sleep(time.Second * )
c2 <- true
}() fmt.Println(<-c1)
fmt.Println(<-c2)
相关讲解:
https://*.com/questions/23233381/whats-the-difference-between-c-makechan-int-and-c-makechan-int-1
https://golang.org/doc/effective_go.html
定义类型和组合类型的区别
定义类型,也可以说是别名和组合类型的区别
有一个Test的结构,NewTest是以Test为类型的一个定义,New2Test和New3Test都是组合类型
type Test struct { N int }
func (m *Test) Name() { fmt.Println("abc")} // NewTest does not inherit any functions of Test
// can access fields
type NewTest Test // New2Test is composite type, it inherit all functions of Test
// can access fields
type New2Test struct {
Test
} // if embedded type is pointer you must initialized it
type New3Test struct {
*Test
}
1.定义类型NewTest 相当于一个新的类型,他不能直接调用Test的方法,但是可以访问Test的字段。如果想调用原类型的方法需要做转换
2.New2Test和New3Test都是组合类型,他俩都可以直接调用Test的方法和访问Test的字段,他俩的不同之处就是一个是值组合一个是指针组合
3.在实例化New3Test的时候需要手动实例化*Test指针
n := NewTest{}
n.N =
// n have no method
// n.Name() // error
v := (*Test)(&n)
v.Name() v2 := Test(n)
v2.Name() n2 := New2Test{}
n2.N =
n2.Name() n3 := New3Test{Test: new(Test)}
// access filed N will panic if you do not initialized *Test
n3.N =
n3.Name()
相关的解答:
https://*.com/questions/28800672/how-to-add-new-methods-to-an-existing-type-in-go/28800807#28800807
https://golang.org/ref/spec#Type_declarations
---