Skip to content

3.6、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}
}