# 第十七章 Golang 结构体 struct

## 类型定义和类型别名

类型定义的语法

```go
type NewType Type
```

示例

```go
package main

import "fmt"

func main() {
    // 定义类型
    type MyInt int

    var i MyInt

    i = 100
    fmt.Printf("%T", i)
    // main.MyInt
}

```


类型别名语法
```go
type NewType = Type
```

示例

```go
package main

import "fmt"

func main() {
    // 定义类型别名
    type MyInt = int

    var i MyInt

    i = 100
    fmt.Printf("%T", i)
    // int
}

```

类型定义和类型别名的区别

- 类型定义相当于定义了一个全新的类型
- 类型别名只会在代码中存在，编译完成后并不会存在
- 类型别名可以使用原来类型所有的方法

## Golang结构体

golang中没有面向对象的概念，但是可以使用结构体来实现，面向对象编程的一些特性，例如：继承、组合等

结构体的定义

```go
type struct_variable_type struct {
    // 成员
    member definition;
    member definition;
    ...
    member definition;
}
```

示例

```go
// 定义一个结构体Person
type Person struct {
    id    int
    name  string
    age   int
    email string
}

// 可以把相同类型的成员合并到一行
type Person struct {
    id, age     int
    name, email string
}
```

声明结构体变量

```go
var tom Person

fmt.Printf("%v", tom)
// {0 0  }

jack := Person{}
fmt.Printf("%v", jack)
// {0 0  }
```

访问结构体成员

```go
package main

import "fmt"

// 定义一个结构体Person

// 可以把相同类型的成员合并到一行
type Person struct {
    id, age     int
    name, email string
}

func main() {
    var tom Person

    tom.id = 12
    tom.name = "Tom"
    tom.age = 23
    tom.email = "123@qq.com"

    fmt.Printf("%v", tom)
    // {12 23 Tom 123@qq.com}

}

```

匿名结构体

```go
package main

import "fmt"


func main() {
    var dog struct {
        id   int
        name string
    }

    dog.id = 12
    dog.name = "Tom"

    fmt.Printf("%v", dog)
    // {12 Tom}

}

```
## Golang结构体的初始化

未初始化的结构体，成员都是零值

使用键值对的方式初始化

```go
package main

import "fmt"

// 定义一个结构体Person
type Person struct {
    id    int
    age   int
    name  string
    email string
}

func main() {
    tom := Person{
        id:    12,
        age:   23,
        name:  "Tom",
        email: "123@qq.com",
    }

    fmt.Printf("%v", tom)
    // {12 23 Tom 123@qq.com}

}

```

使用值的列表初始化

```go
package main

import "fmt"

// 定义一个结构体Person
type Person struct {
    id    int
    age   int
    name  string
    email string
}

func main() {
    tom := Person{
        12,
        23,
        "Tom",
        "123@qq.com",
    }

    fmt.Printf("%v", tom)
    // {12 23 Tom 123@qq.com}

}

```

部分成员初始化

```go
package main

import "fmt"

// 定义一个结构体Person
type Person struct {
    id    int
    age   int
    name  string
    email string
}

func main() {
    tom := Person{
        id:   12,
        name: "Tom",
    }

    fmt.Printf("%v", tom)
    // {12 0 Tom }

}

```




## Golang结构体的指针

普通指针

```go
package main

import "fmt"

// 定义一个结构体Person
type Person struct {
    id    int
    age   int
    name  string
    email string
}

func main() {
    // 定义字符串变量
    var name string
    name = "Tom"

    // 定义字符串类型的指针变量
    var name_ptr *string
    name_ptr = &name

    // 变量的值
    fmt.Printf("%v\n", name)
    // Tom

    // 指针地址
    fmt.Printf("%v\n", name_ptr)
    // 0xc000014250

    // 指针指向的变量值
    fmt.Printf("%v\n", *name_ptr)
    // Tom
}

```

结构体的指针

```go
package main

import "fmt"

// 定义一个结构体Person
type Person struct {
    id    int
    age   int
    name  string
    email string
}

func main() {

    // 结构体变量
    person := Person{
        id:    11,
        name:  "Tom",
        age:   23,
        email: "123@qq.com",
    }

    // 指针变量
    var person_ptr *Person
    person_ptr = &person

    // 变量的值
    fmt.Printf("%v\n", person)
    // {11 23 Tom 123@qq.com}

    // 指针地址
    fmt.Printf("%p\n", person_ptr)
    // 0xc00009a180

    // 指针指向的变量值
    fmt.Printf("%v\n", *person_ptr)
    // {11 23 Tom 123@qq.com}
}

```

使用new关键字创建结构体指针

```go
package main

import "fmt"

// 定义一个结构体Person
type Person struct {
    id    int
    age   int
    name  string
    email string
}

func main() {

    var person = new(Person)

    // 访问结构体指针成员，可以省略*
    // 自动解引用
    (*person).age = 23
    person.id = 12
    person.name = "Tom"

    fmt.Printf("%T\n", person)
    // *main.Person

    fmt.Printf("%v\n", person)
    // &{12 0 Tom }

    fmt.Printf("%v\n", *person)
    // {12 0 Tom }
}

```

## 结构体作为函数参数

结构体作为参数传递给函数

- 直接传递结构体，传递的是副本，函数内部不能改变外部结构体内容
- 传递结构体指针，函数内部能改变外部结构体内容

值传递

```go
package main

import "fmt"

type Person struct {
    age  int
    name string
}

// 值传递，拷贝了一份副本
func showPerson(person Person) {
    person.age = 12
    person.name = "Jakc"

    fmt.Printf("%v\n", person)
}

func main() {

    person := Person{
        age:  11,
        name: "Tom",
    }

    fmt.Printf("%v\n", person)
    // {11 Tom}

    showPerson(person)
    // {12 Jakc}

    fmt.Printf("%v\n", person)
    // {11 Tom}
}

```

指针传递

```go
package main

import "fmt"

type Person struct {
    age  int
    name string
}

// 指针传递
func showPerson(person *Person) {
    person.age = 12
    person.name = "Jakc"

    fmt.Printf("%v\n", *person)
}

func main() {

    person := Person{
        age:  11,
        name: "Tom",
    }

    fmt.Printf("%v\n", person)
    // {11 Tom}

    showPerson(&person)
    // {12 Jakc}

    fmt.Printf("%v\n", person)
    // {12 Jakc}
}

```
## 结构体的嵌套

示例

```go
package main

import "fmt"

type Dog struct {
    age  int
    name string
}

type Person struct {
    age  int
    name string
    dog  Dog
}

func main() {
    dog := Dog{
        age:  1,
        name: "dog",
    }

    person := Person{
        dog:  dog,
        age:  11,
        name: "Tom",
    }

    fmt.Printf("%v\n", person)
    // {11 Tom {1 dog}}

    fmt.Printf("%v\n", person.dog.name)
    // dog
}


```

## 方法

golang中，方法是一种特殊的函数

定义与struct之上（与struct关联、绑定），被称为struct的接受者（receiver）

通俗讲，方法就是有接收者的函数

语法格式

```go
type struct_name struct{}

func (recv struct_name) method_name(param_type) return_type{}

func (recv *struct_name) method_name(param_type) return_type{}
```



```go
package main

import "fmt"

// 属性和方法分开写
type Person struct {
    age  int
    name string
}

// Person 接收者 receiver
func (person Person) eat() {
    fmt.Printf("%v eat\n", person.name)
}

func (person Person) sleep() {
    fmt.Printf("%v sleep\n", person.name)
}

func main() {

    person := Person{
        age:  11,
        name: "Tom",
    }

    person.eat()
    // Tom eat

    person.sleep()
    // Tom sleep
}

```

go语言方法的注意事项

- 方法的receiver type可以是类型别名、struct、slice、map、channel、func等
- struct结合它的方法等价于面向对象中的类，只不过struct可以和它的方法分开写，并非一定要属于同一个文件，但是，必须属于同一个包
- 方法有两种接收类型`(T type)` 和 `(T *Type)`，它们之间有区别
- 方法就是函数，所有Go中没有方法重载overload的说法，也就是说同一个类型中的所有方法名必须唯一
- 如果receiver是一个指针类型，则会自动解除引用
- 方法和type是分开的，意味着实例的行为behavior和数据存储field是分开的，但它们通过receiver建立起关联关系

## 方法接收者类型

- 值类型：复制结构体副本
- 指针类型：不复制

```go
package main

import "fmt"

// Person 接收者 receiver
type Person struct {
    age  int
    name string
}

// 值传递，拷贝了一份副本
func (person Person) showPersonByValue() {
    person.age = 12
    person.name = "Jack"

    fmt.Printf("%v\n", person)
}

// 指针传递
func (person *Person) showPersonByPointer() {
    person.age = 12
    person.name = "Jack"

    fmt.Printf("%v\n", person)
}

func main() {

    person := Person{
        age:  11,
        name: "Tom",
    }

    fmt.Printf("%v\n", person)
    // {11 Tom}

    // 接收者类型是变量，值没有被修改
    person.showPersonByValue()
    // {12 Jack}

    fmt.Printf("%v\n", person)
    // {11 Tom}

    // 接收者类型是指针，值被修改
    person.showPersonByPointer()
    // &{12 Jack}

    fmt.Printf("%v\n", person)
    // {12 Jack}
}
```