[go]method的指针声明及非指针声明

以下大部分为搬运过程重写代码。原文:https://segmentfault.com/a/1190000003772144


method可以为一个type添加(声明)一个方法,例如:

type Cat struct {
}

func (c Cat) Hwo() {
    fmt.Println("Miah!")
}

即对Cat类型(结构体)添加Hwo方法,使其在叫唤的时候可以发出声音, 添加方法的代码表现比java好很多(extend),也比python好(需重新声明一个Class)。


按官方的spec可以对应到如上的例子的两种声明:

func (c Cat) Hwo()
func (c *Cat) Hwo()

两种有什么区别呢?

package main

import (
    "fmt"
)

type Cat struct {
    age int
}

func (c Cat) AddAge() {
    fmt.Println("add age!")
    fmt.Println(c.age + 10)
    c.age += 1
}

func (c *Cat) AddOneAge() {
    c.age += 1
    fmt.Println("add one age!")
}

func main() {
    cat := &Cat{1}
    fmt.Println(cat)
    cat.AddAge()
    fmt.Println(cat)
    cat.AddOneAge()
    fmt.Println(cat)
}

结果:

&{1}
add age!
11
&{1}
add one age!
&{2}

修改cat声明方式为:

cat := Cat{1}

结果:

{1}
add age!
11
{1}
add one age!
{2}

发生了什么?
1. cat变量是一个指针,可以用reflect.Typeof(cat)看出来;
2. (c Cat)添加的方法AddAge()被执行了,能获取cat的值,但是未改变cat指针指向的内存区块的值;
3. (c *Cat)添加的方法AddOneAge()被执行了,改变cat指针指向的内存区块的值;
4. 重新定义catb := Cat{1},似乎catb非指针,但是还是一样的结果(除了变量部分)。

如何解读?
1. 无论是将一个变量声明为指针还是非指针,go在method上对待它们的态度都是一致的;
2. 声明method时,传入(c *Cat)的声明方式才能修改new出来的对象(cat :=),因为method的处理对象是一个Cat类型的指针。
3. 在声明变量时,建议声明为指针对象cat := &Cat{1},这样做有好处

a.传递指针有卓有成效的,特别是传递大型变量时
b.大部分科学的代码是传递指针,为了代码的一致性最好都用指针

详见Reference.3

题外话,这里有个例子可以阐明一个状况
对于interface来说,识别method时会辨别(c Cat)及(c *Cat)的区别: http://play.golang.org/p/-g44WHg_uT

Reference


  1. http://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/
  2. http://golang.org/ref/spec#Method_declarations
  3. http://golang.org/doc/faq#methods_on_values_or_pointers

golang中结构体的初始化方法(new方法)

1、自定义一个结构体

type Vertex struct {
    X, Y float64
}

2、初始化方法-指针:

rect1 := new(Vertex )
rect2 := &Vertex {}
rect3 := &Vertex {1, 2}
rect4 := &Vertex {X:100, Y:200}

注意: 这几个变量全部为指向Rect结构的指针(指针变量),因为使用了new()函数和&操作符.

3、初始化方法-类型实例

a := Rect{}
b := Rect{3, 4}
c := Rect{X=5, Y=6}

则表示这个是一个Rect{}类型.两者是不一样的.

4、区别
下面这个例子能展现之间区别:

package main
import "fmt"

type Vertex struct {
        X, Y float64
} 
func main() {
    rect1 := new(Vertex)
    rect2 := &Vertex{1, 2}
    fmt.Printf("%v  %T  %v \n",  rect1,  rect1,  *rect1)
    fmt.Printf("%v  %T  %v \n",  rect2,  rect2,  *rect2)

    rect3 := Vertex{X: 5, Y: 6}
    fmt.Printf("%v  %T\n",  rect3,  rect3)

}

// 输出:
/*
&{0 0}  *main.Vertex  {0 0} 
&{1 2}  *main.Vertex  {1 2} 
{5 6}  main.Vertex
*/

从结果中可以清楚的看到两者的不同.

用 new 分配内存 内建函数 new 本质上说跟其他语言中的同名函数功能一样:new(T) 分配了零值填充的 T 类型的内存空间,并且返回其地址,一个 *T 类型的值。用 Go 的术语说,它返回了一个指针,指向新分配的类型 T 的零值。记住这点非常重要。 这意味着使用者可以用 new 创建一个数据结构的实例并且可以直接工作。

务必记得 make 仅适用于 map,slice 和 channel,并且返回的不是指针。应当用 new获得特定的指针。

原文: http://fuhao715.github.io/2014/03/25/golang-struct-init-new.html