Go语言程序设计(十二)结构体

        前面的章节已经介绍了Go语言的基本数据类型,以及一种构造数据类型一一数组,数组中的各元素是属于同一种类型的。有时需要将不同类型的数据项组织在一.起,以便于引用,这种数据类型就是结构体(Struct)。结构体中的数据项,通常被称为字段(Field)。
        结构体中的字段都是互相联系的,比如一个学生结构体里包含学号、姓名、年龄、性别、班级等字段,这些字段都是与某个学生相联系的。

一、结构体的定义

在Go语言中,可以使用关键字“type”定义新的数据类型,另外还有关键字“struct”。

基本格式如下:

type structName struct {
    field1 类型
    field2 类型
}

在定义结构体时要注意:

  • (1)结构体的命名规则和变量名相同,遵循标识符命名规则。
  • (2)结构体的命名规则还遵循可见性命名规则,即只有首写字母为大写的结构体和属性才能在包外被访问。
  • (3)结构体内的所有属性使用一对“{}”括起来,左大括号“{”必须和关键字struct 放在同一行,右大括号“}”必须单独占一行。

1、在定义结构体时,字段的数据类型可以是Go语言支持的任何基本数据类型。

2、在定义结构体时,每个字段都应有一个名字。如果某些字段根本不会被使用,只是为了以后对结构体进行扩展预留的,这种字段叫做预留字段,使用标识符“_”来命名。

type people struct {
    name string
    age int
    _ string
}

3、在Go语言中,结构体本身也是一种数据类型,所以也可以使用结构体作为字段的类型。

二、结构体变量

        在Go语言中,由于结构体也是一种值类型,所以可以使用它来定义结构体类型的变量,即结构体变量(Variableofthestructtype)。用户也可以像使用普通变量--样对结构体变量进行各种各样的操作,比如赋值操作,关系操作等。

1、结构体变量的声明

        当定义一个结构体后,就可以使用它来声明一个结构体变量了。使用“var”关键字来进行定义:

var stu student

        可以按如下方式进行简写:

stu := student{}

        在使用关键字“var”声明结构体变量时,系统会自动为其分配内存空间。在使用第二种简写的方式时,必须在后面跟上一对大括号“{}”,表示声明变量的同时为其初始化存储空间。

2、字段的访问

        声明结构体变量后,就可以使用点号“.”操作符来访问结构体中的字段了,可以通过这种方式直接读取字段值或者对字段进行赋值。

例如:

stu.name = "李明"
stu.age = 18

三、结构体对象

        Go语言中没有类(Class)的概念,但并不表示Go不支持面向对象编程,Go是用结构体来实现面向对象编程模型的。在Go语言中,可以使用new()函数创建一个结构体对象(Objectofthestructtype),并为其分配存储空间,调用成功后new()函数会返回一个指向该结构体对象的地址指针。

例如:

var stu * student
stu = new(student)

// 或者
stu := new(student)

        该例中,变量stu是指向结构体对象(student)的一个指针,在stu的存储空间中,student的各字段都被初始化为该类型的零值。

        在使用new来创建一个结构体对象时,它可以出现在程序的任何位置。比如需要在包内声明一个结构体变量,但又不想在程序的最开始为其分配内存,那么这种操作方式就显得特别灵活。

1、结构体对象初始化

当声明一个结构体变量或者创建一个结构体对象后,如果没有初始化,结构体中的各个字段都将被设置为类型零值。

Go语言提供两种方式初始化:全部初始化和部分初始化

全部初始化:各字段按照结构体定义的顺序依次给出,示例:

stul : = student{ 13001, "李明",false,19,"网络01"}
stu2 : = &student{13002,"张晓",true, 18,"网络01"}

部分初始化:使用“字段名:字段值”的方式对部分字段进行初始化:

stu3 : = student{ name: "王乐" ,age: 19}
stu4 : = &student{ name: "赵琼",id: 13003}

这种方式可以不用根据结构体定义的字段顺序来。

2、结构体的赋值和关系操作

在Go语言中,相同类型的结构体之间可以直接通过使用“=”操作符来复制赋值。

四、嵌入式结构

        Go语言还支持嵌入式结构( Embedded Struct),所谓嵌入式结构就是没有定义名字的结构体。嵌入式结构可以作为结构体的一个字段,或者可以直接使用嵌入式结构定义结构体变量。

1、嵌入式结构用作字段

type student struct {
    id    int
    name  string
    sex   bool
    class string
    birthday struct {    //使用嵌入式结构定义birthday字段
        year int
        month int
        day int
    }
}

该例中,结构体student的字段brthday就是使用嵌人式结构定义的。

2、嵌入式结构直接定义结构体变量

        在Go语言中,可以不事先声明结构体,而直接使用嵌入式结构定义结构体变量。由于嵌入式结构没有名字,所以使用它定义结构体变量的同时要进行初始化。

var usr = struct {
    id    int
    name  string
    password string
}{name: "张三"}

也可以使用new()函数创建结构体,并为其分配存储空间,usr的三个字段会被初始化为类型零值:

var usr = new( struct {
    id    int
    name  string
    password  string
})

3、嵌入式结构用于Map

map1 : = map[ string ] struct {
                name string
                age int
            }{
                "teacher": {"郑智",39},
                "student": {"李明",18},
             }

        该例中使用嵌入式结构定义Map的Value 类型,这里注意,在使用嵌人式结构定义Map时同样要进行初始化工作。

五、匿名字段

        在前面介绍了结构体的定义方法,定义时是字段名与其类型一一对应。另外,Go语言还支持只提供类型,而不写字段名的定义方式,这就是匿名字段( Anonymous field)。匿名字段是将结构体或指针嵌入到另一个结构体中,但不提供字段名,也被称为嵌入字段。需要注意的是,匿名字段的嵌入并不是继承,Go语言中根本没有继承的概念。

type people struct {
    name string
    sex bool
}

type teacher struct {
    people    //匿名字段
    department string
}

        该例首先定义了一个结构体people, 然后定义了另外一个结构体teacher。而在定义teacher时,people直接作为匿名字段嵌入到teacher中,那么teacher 将包含people的所有字段。

1、匿名字段的初始化

        Go语言的匿名字段和大多数面向对象语言(OOP)实现继承的方式有些类似,但其本质就是隐式定义了一个以类型为名称的字段。由于是内容嵌入,所以在内存结构上外层结构体和内嵌结构体仍然是一个整体。所以,在初始化结构体成员时,依然要把匿名字段的成员当作一个正常的字段来赋值。

2、匿名字段的访问

        Go语言对点号“.”操作符做了特殊处理,利用它可以直接访问或修改匿名字段成员。

        无论是结构体中的正常字段还是匿名字段,都可以直接使用点号“.”操作符来访问,就好像匿名字段就是结构体本身的字段一样。就算是被嵌人匿名字段也包含匿名字段,依然可以用点号“.”操作符直接访问任意嵌入层级的成员,编译器总是从外向里进行查找,直到完成任务或出错。

        无论嵌入多少层,匿名字段中的成员访问方法和正常字段一样。Go语言中,就是利用匿名字段的这种访问特性,实现字段的继承。

3、匿名字段的多种形式

        在Go语言中,除了结构体本身,其他所有的内置数据类型和自定义类型都可以作为匿名字段。

        不仅struct能作为结构体的匿名字段,自定义类型、内置数据类型都可以作为结构体的匿名字段,而且可以在相应的字段上进行函数操作。

4、匿名字段的重名

        匿名字段的嵌套可能导致在不同层级上出现同名字段,这种情况会导致一些系统隐藏行为的发生。
(1)外部字段会隐藏匿名字段同名成员。举例说明,如果people 里面有一个phone字段,而teacher里也有一个phone字段,当要访问phone时,Go语言会按照最外层优先的原则。也就是当通过teacher.phone访问时,是访问teacher里的字段,而不是people里的字段。当然如果想要访问重载后对应匿名字段里的成员,可以通过匿名字段类型前缀来访问。

(2)如果多个匿名字段在同一层次重名,将导致编译器无法确定目标而出错。例如,结构体d1、d2中定义了一个int型字段x,而d1、d2又最为结构体data的嵌入式匿名字段,那么在data的内存结构中就存在两个同层级的字段x。如果进行访问,将导致编译错误。

5、匿名指针类型

        匿名字段还可以作为指针类型嵌人到结构体中,其访问方式和直接嵌入匿名字段相同。

六、字段标签

        Go语言允许为字段定义标签(Tag) ,Struct中的不同字段不仅可以通过字段名和类型区分,还可以通过字段标签(Field-tag)来区分。字段标签通常用于程序使用手册的书写,或者作为一些重要的标记符。
        在Go语言中,不允许在普通程序语句中访问字段标签,字段标签只能使用反射包(Reflect)中提供的特殊方法进行读取,利用Reflect可以获取字段名、字段类型和字段值。如果是Struct类型,Reflect就可以使用字段名作为索引读取字段标签。

//字段标签
package main

import(
    "fmt"
    "reflect"
)

type user struct {
    Id    int    "账号"
    Name  string "姓名”
    Sex   bool   "性别"
}

func main() {
    u : = user{100,"张三”,false}
    //使用TypeOf()函数获取对象的类型
    t := reflect. Type0f(u)
    //使用Vailue0f()获取对象的值
    v := reflect. ValueOf(u)
    fori: =0; i< t. NumField(); i++ {
        f := t. Field(i)
        fmt. Printf("名s(名s= *v)\n",f. Tag, f. Name, v. Field(i). Interface())
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值