Go结构体类型的使用

2016-06-24  本文已影响3243人  喜龙爱慧
老人与海&欧加琪 作品

我们花了两年学会说话,却要花上六十年来学会闭嘴。 by 佚名

引用于:http://wufazhuce.com/one/1384

结构体定义

type identifier struct {
    field1 type1
    field2 type2
    ...
}

使用 new 初始化结构体

使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:var t *T = new(T)或者t := new(T),另外,用&T{},也同样等价于用new,因为底层仍然会调用new(T)

type Point struct {
    x int
    y int
}

func Test_demo1(t *testing.T) {
    intr1 := new(Point)
    intr1.x = 2
    intr1.y = 6
    intr2 := &Point{0, 3}
    intr3 := &Point{x: 5, y: 1}
    intr4 := &Point{y: 5}

    fmt.Println(intr1) //&{2 6}
    fmt.Println(intr2) //&{0 3}
    fmt.Println(intr3) //&{1 5}
    fmt.Println(intr4) //&{0 5}
}
使用 new 初始化 结构体字面量初始化

实例化结构体两种常用形式:
1:用 new(T) 实例化。
2:用 &T{} 实例化。(推荐)

使用工厂模式创建结构体实例

func NewPoint(x int, y int) *Point {
    if x < 0 || y < 0 {
        return nil
    }
    return &Point{x, y}
}

func Test_demo2(t *testing.T) {
    p1 := NewPoint(-1, 2)
    fmt.Println(p1) //nil

    p2 := NewPoint(2, 2)
    fmt.Println(p2) //&{2 2}
}

上面的示例演示的就是工厂模式创建结构体实例,在Go标准包有很多地方应用的该模式。可以把这种模式等同为面向对象语言中的构造函数。另外,如果把 Point 结构体,命名变成小写,即point,就可以作为私有变量,强制实例化结构体必须使用工厂模式。

反射获取结构体的标签

type Point struct {
    x int "this is x"
    y int "this is y"
}

func Test_demo3(t *testing.T) {
    //p := &Point{1, 2} //会报运行时错误
    p := Point{1, 2}
    pType := reflect.TypeOf(p)
    pField := pType.Field(0) 
    fmt.Printf("%v\n", pField.Tag) //this is x
}

结构体的方法

一个类型加上它的方法等价于面向对象中的一个类。通常情况下,这个类型一般定义为结构体类型。

定义方法的格式

func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }

其中,recv可称之为这个方法methodName接收者receiver_type接收者类型。该类型可以是除了interface类型的任何类型,但是通常都用结构体类型。

在接收者是指针时,方法可以改变接收者的值(或状态),当然这点函数也可以做到(当参数作为指针传递,即通过引用调用时,函数也可以改变参数的状态)。但如果接受者不是指针,方法是无法改变接收者的状态的,方法内部应用的接收者状态,仅是接收者实例的拷贝

指针方法和值方法都可以在指针或非指针上被调用,Go会自动转换。

type Info struct {
    address string
    phone string
}

//接收者是非指针
func (this Info) changeInfo() {
    this.address = "上海"
    this.phone = "10086"
    fmt.Println(this)
}

//接收者是指针
func (this *Info) changeInfo2() {
    this.address = "上海"
    this.phone = "10086"
    fmt.Println(this)
}

func Test_demo5(t *testing.T) {
    info := &Info{"北京", "10086"}
    //指针调用,但接收者自动被转换成了非指针,changeInfo函数应用的是info的拷贝。
    info.changeInfo() // {上海 10086}
    fmt.Println(info) //&{北京 10086}

    info2 := Info{"北京", "10086"}
    //非指针调用,但接收者自动被转换成了指针,changeInfo函数应用的是真正的info实例。
    info2.changeInfo2() // &{上海 10086}
    fmt.Println(info2) //{上海 10086}
}

在 Test_demo5 中第一种调用接收者是指针类型,但调用changeInfo方法时,Go自动转换成非指针类型,即实例的拷贝,所以在方法体内的改变Info的属性值,其实修改的是拷贝版,原实例并不受影响。而第二种调用接收者是非指针类型,但是Go自动转换成指针类型,所以内部的修改会影响实例本身属性状态。

所以:**如果想通过方法改变接收者实例本身的属性值或者状态,那么方法的接收者必须是指针类型。

结构体方法定义的常用规范:
*func (this T) methodName(parameter_list) (return_value_list) { ... }

类的公有/私有属性和方法

Go规定命名的属性如果首字母大写,即可以被非本go文件程序调用。所有公有的属性和方法,命名首字母必须大写。反之,便是私有的。

类的继承

type Human struct {
    Name string
}

func (this *Human) eat() {
    fmt.Println(this.Name + "要吃饭.")
}

type Man struct {
    Human //继承了Human类
}

func (this *Man) chageName(name string) {
    this.Name = name
}

func Test_demo6(t *testing.T) {
    man := &Man{Human{"人类"}}
    man.chageName("男人")
    man.eat() //男人要吃饭.
}

读取运行内存

func Test_demo7(t *testing.T) {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    fmt.Printf("%d Kb\n", m.Alloc/1024) //单位Kb
}
上一篇下一篇

猜你喜欢

热点阅读