Bootstrap

go中Struct 结构体详解

目录

一、结构体定义

1、结构体的定义

2、结构体字段的可见性

3、结构体的匿名字段

二、结构体实例化

1、基本实例化

2、针类型实例化

3、取地址实例化

三、结构体初始化

1、两种初始化的方式

2、使用“键值对",值列表两种初始化

四、使用结构体实现构造函数

五、结构体的“继承”


一、结构体定义

        Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。 也就是我们可以通过struct来定义自己的类型了。

Go语言中通过struct来实现面向对象。

1、结构体的定义

type 类型名 struct {
    字段名 字段类型
    字段名 字段类型
    …
}

1.类型名:标识自定义结构体的名称,在同一个包内不能重复。

2.字段名:表示结构体字段名。结构体中的字段名必须唯一。

3.字段类型:表示结构体字段的具体类型。

举个例子,我们定义一个Person(人)结构体,代码如下:

type person struct {
    name string
    city string
    age  int8
}

同样类型的字段也可以写在一行,

type person1 struct {
    name, city string
    age        int8
}

这样我们就拥有了一个person的自定义类型,它有name、city、age三个字段,分别表示姓名、城市和年龄。这样我们使用这个person结构体就能够很方便的在程序中表示和存储人信息了。

2、结构体字段的可见性

结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。

3、结构体的匿名字段

结构体允许其成员字段在声明时只有类型没有字段名,这种没有名字的字段就称为匿名字段。

//Person 结构体Person类型
type Person struct {
    string
    int
}

func main() {
    p1 := Person{
        "pprof.cn",
        18,
    }
    fmt.Printf("%#v\n", p1)        //main.Person{string:"pprof.cn", int:18}
    fmt.Println(p1.string, p1.int) //pprof.cn 18
    
}

        匿名字段默认采用类型名作为字段名,由于结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。

二、结构体实例化

notes:如果先声明、后实例化。则实例化后才能使用struct

结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存。因此必须在定义结构体并实例化后才能使用结构体的字段。

实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的内存是完全独立的。

1、基本实例化

结构体本身是一种类型,可以像整型、字符串等类型一样,以 var 的方式声明结构体即可完成实例化。

var ins T
type person struct {
    name string
    city string
    age  int8
}

func main() {
    var p1 person
    p1.name = "pprof.cn"
    p1.city = "北京"
    p1.age = 18
    fmt.Printf("p1=%v\n", p1)  //p1={pprof.cn 北京 18}
    fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"pprof.cn", city:"北京", age:18}
}

2、针类型实例化

Go 语言中,还可以使用 new 关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。

使用 new 的格式如下:

ins := new(T)

Go 语言让我们可以像访问普通结构体一样使用.访问结构体指针的成员。

type Player struct{
    Name string
    HealthPoint int
    MagicPoint int
}

//指针类型
tank := new(Player)

tank.Name = "Canon"
tank.HealthPoint = 300

经过 new 实例化的结构体实例在成员赋值上与基本实例化的写法一致。

3、取地址实例化

          在 Go 语言中,对结构体进行&取地址操作时,视为对该类型进行一次 new 的实例化操作。取地址格式如下:

ins := &T{}

下面使用结构体定义一个命令行指令(Command),指令中包含名称、变量关联和注释等。对 Command 进行指针地址的实例化,并完成赋值过程,代码如下:

type Command struct {
    Name    string    // 指令名称
    Var     *int      // 指令绑定的变量
    Comment string    // 指令的注释
}

var version int = 1

cmd := &Command{}

cmd.Name = "version"
cmd.Var = &version
cmd.Comment = "show version"

重点:取地址实例化是最广泛的一种结构体实例化方式。

func newCommand(name string, varref *int, comment string) *Command {
    
    return &Command{
        Name:    name,
        Var:     varref,
        Comment: comment,
    }
}

cmd = newCommand(
    "version",
    &version,
    "show version",
)

三、结构体初始化

结构体在实例化时可以直接对成员变量进行初始化。相当于实例化时直接赋值

1、两种初始化的方式

初始化也有两种方式,直接初始化,取地址初始化

//1、直接实例化
p5 := person{
    name: "pprof.cn",
    city: "北京",
    age:  18, //最后一个逗号必须存在,否则会报错
}

//2、取地址实例化
p6 := &person{
    name: "pprof.cn",
    city: "北京",
    age:  18, //最后一个逗号必须存在,否则会报错
}

//备注:没有指针类型的初始化

2、使用“键值对",值列表两种初始化

一种是字段“键值对”形式及多个值的列表形式。键值对形式的初始化适合选择性填充字段较多的结构体;

多个值的列表形式适合填充字段较少的结构体。(说白了就是不写key,直接写value)

(1)使用“键值对”初始化结构体

键值对的填充是可选的,不需要初始化的字段可以不填入初始化列表中。

结构体实例化后字段的默认值是字段类型的默认值,例如:数值为 0,字符串为空字符串,布尔为 false,指针为 nil 等。

键值对初始化的格式如下:

ins := 结构体类型名{
    字段1: 字段1的值,
    字段2: 字段2的值,
    …
}

//示例一:
p5 := person{
    name: "pprof.cn",
    city: "北京",
    age:  18, //最后一个逗号必须存在,否则会报错
}

fmt.Printf("p5=%#v\n", p5) //p5=main.person{name:"pprof.cn", city:"北京", age:18}

//示例二:也可以对结构体指针进行键值对初始化
p6 := &person{
    name: "pprof.cn",
    city: "北京",
    age:  18, //最后一个逗号必须存在,否则会报错
}
fmt.Printf("p6=%#v\n", p6) //p6=&main.person{name:"pprof.cn", city:"北京", age:18}

(2)使用值的列表初始化

初始化结构体的时候可以简写,也就是初始化的时候不写键,直接写值:

p8 := &person{
    "pprof.cn",
    "北京",
    18,
}
fmt.Printf("p8=%#v\n", p8) //p8=&main.person{name:"pprof.cn", city:"北京", age:18}

使用这种格式初始化时,需要注意:

  • 必须初始化结构体的所有字段。
  • 初始值的填充顺序必须与字段在结构体中的声明顺序一致。
  • 该方式不能和键值初始化方式混用。(也就是不能一下写键,一下不写键)

四、使用结构体实现构造函数

go的类型或结构体没有构造函数的功能。结构体的初始化过程可以使用函数封装实现。

因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型。

func newPerson(name, city string, age int8) *person {
    return &person{
        name: name,
        city: city,
        age:  age,
    }
}

调用构造函数

p9 := newPerson("pprof.cn", "测试", 90)
fmt.Printf("%#v\n", p9)

五、结构体的“继承”

Go语言中使用结构体也可以实现其他编程语言中面向对象的继承。

说明:这种只是模拟“继承”

//Animal 动物
type Animal struct {
    name string
}

func (a *Animal) move() {
    fmt.Printf("%s会动!\n", a.name)
}

//Dog 狗
type Dog struct {
    Feet    int8
    *Animal //通过嵌套匿名结构体实现继承
}

func (d *Dog) wang() {
    fmt.Printf("%s会汪汪汪~\n", d.name)
}

func main() {
    d1 := &Dog{
        Feet: 4,
        Animal: &Animal{ //注意嵌套的是结构体指针
            name: "乐乐",
        },
    }
    d1.wang() //乐乐会汪汪汪~
    d1.move() //乐乐会动!
}

 

;