美文网首页
Go结构体类型的使用

Go结构体类型的使用

作者: 喜龙爱慧 | 来源:发表于2016-06-24 14:39 被阅读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
    }
    

    相关文章

      网友评论

          本文标题:Go结构体类型的使用

          本文链接:https://www.haomeiwen.com/subject/ggqodttx.html