Go语言结构体

1.struct的定义

  • 1.Go中面向对象是通过struct来实现的,struct是用户自定义类型
type User struct{
    Username string
    Sex string
    Age int
    AvatarUrl string
}

2.struct的初始化

// 方式一:
var user User
user.Username = "joke"
user.Sex = "M"
user.Age = 21
user.AvatarUrl = "https://www.person/1/profile/avat.png"

// 方式二:
user := User {
    Username: "lucy",
    Age: 17,
    Sex: "M",
    // AvatarUrl: "https://www.person/15/profile/avat.png",
}

3.定义的同时初始化

  • 第一种赋值方式(结构体被调用时,所有成员会进行复制,不推荐使用
var user User = User {
    Username: "tony",
    Age: 29,
    Sex: "F",
    AvatarUrl: "https://www.person/27/profile/avat.png",
}
fmt.Println(user)  // {tony F 29 https://www.person/27/profile/avat.png}
  • 第二种赋值方式
var user *User = &User{}
(*user).Username = "haili"
// 编译器会自动将其转化为(*user).Age
user.Age = 35
fmt.Printf("%#v", *user)  // main.User{Username:"haili", Sex:"", Age:35, AvatarUrl:""}

var user *User = &User {
    Username: "tony",
    Age: 29,
    Sex: "F",
    AvatarUrl: "https://www.person/27/profile/avat.png",
}
fmt.Println(*user)  // {tony F 29 https://www.person/27/profile/avat.png}
fmt.Println(user.Age)  // 29
  • 第三种赋值方式(new返回的也是一个内存地址)
// new(User)  相当于  &User{}
var user *User = new(User)
user.Age = 32
user.Username = "tom"
user.Sex = "F"
user.AvatarUrl = "https://www.person/4/profile/avat.png"
fmt.Printf("%#v", *user)  // main.User{Username:"tom", Sex:"F", Age:32, AvatarUrl:"https://www.person/4/profile/avat.png"}

4.行为(方法)的定义

  • 第一种:值拷贝:实例对应的方法被调用时,实例成员会被进行复制(不推荐使用
package main

import (
    "fmt"
    "unsafe"
)

type User struct{
    Username string
    Sex string
    Age int
    AvatarUrl string
}

func (u User) UserInfo() string {
    // 查看传递进来的结构体的Username的地址
    fmt.Printf("Address is %x\n", unsafe.Pointer(&u.Username))  // c00001a100
    // 相当于(*u).Username,编译器帮我们做了这些工作
    return fmt.Sprintf("Name:%s-Age:%d-Sex:%s", u.Username, u.Age, u.Sex)

}

func main()  {
    // user := &User{"lucy", "M", 17, "https://www.person/15/profile/avat.png"}  // 这样也是支持的
    user := User{"lucy", "M", 17, "https://www.person/15/profile/avat.png"}
    fmt.Printf("Address is %x\n", unsafe.Pointer(&user.Username))  // c00001a0c0
    fmt.Println(user.UserInfo())  // Name:lucy-Age:17-Sex:M
}
package main

import (
    "fmt"
    "unsafe"
)

type Stu struct {
    Name string
    Age int
}

func (stu Stu) Set(name string, age int) {
    // go会为此处的stu新开辟一块内存地址,和调用处的stu不是一个东西
    fmt.Printf("Address is %x\n", unsafe.Pointer(&stu.Age))  // c000004510
    stu.Name = name
    stu.Age = age
}

func main()  {
    stu := Stu{
	Name: "lucy",
	Age: 22,
    }
    fmt.Println(stu)  // {lucy 22}
    fmt.Printf("Address is %x\n", unsafe.Pointer(&stu.Age))  // c0000044b0
    stu.Set("jack", 25)
    fmt.Println(stu)  // {lucy 22}
}
  • 第二种:引用拷贝:避免内存拷贝(推荐使用)
package main

import (
    "fmt"    
    "unsafe"
)

type User struct{
    Username string
    Sex string
    Age int
    AvatarUrl string
}

func (u *User) UserInfo() string {
    // 查看传递进来的结构体的Username的地址
    fmt.Printf("Address is %x\n", unsafe.Pointer(&u.Username))  // c00001a0c0
    // 相当于(*u).Username,编译器帮我们做了这些工作
    return fmt.Sprintf("Name:%s-Age:%d-Sex:%s", u.Username, u.Age, u.Sex)

}

func main()  {
    // user := &User{"lucy", "M", 17, "https://www.person/15/profile/avat.png"}  // 这样也是支持的
    user := &User{"lucy", "M", 17, "https://www.person/15/profile/avat.png"}
    fmt.Printf("Address is %x\n", unsafe.Pointer(&user.Username))  // c00001a0c0
    fmt.Println(user.UserInfo())  // Name:lucy-Age:17-Sex:M
}
package main

import (
    "fmt"
    "unsafe"
)

type Stu struct {
    Name string
    Age int
}

func (stu *Stu) Set(name string, age int) {
    fmt.Printf("Address is %x\n", unsafe.Pointer(&stu.Age))  // c0000044b0
    stu.Name = name
    stu.Age = age
}

func main()  {
    stu := &Stu{
	Name: "lucy",
	Age: 22,
    }
    // stu := Stu{  这样也可以,主要看方法
    //     Name: "lucy",
    // 	   Age: 22,
    }
    fmt.Println(*stu)  // {lucy 22}
    fmt.Printf("Address is %x\n", unsafe.Pointer(&stu.Age))  // c0000044b0
    stu.Set("jack", 25)  // 修改成功
    fmt.Println(*stu)  // {jack 25}
}

5.struct的内存布局

  • 结构体的内存布局:占用一段连续的内存空间
package main

import "fmt"

type Test struct{
    A int32  // 4字节
    B int32
    C int32
    D int32
}

func main()  {
    var t Test
    fmt.Printf("%p\n",&t)  // 0xc0000100d0
    fmt.Printf("%p\n", &t.A)  // 0xc0000100d0
    fmt.Printf("%p\n", &t.B)  // 0xc0000100d4
    fmt.Printf("%p\n", &t.C)  // 0xc0000100d8
    fmt.Printf("%p\n", &t.D)  // 0xc0000100dc
}

6.构造函数

package main

import "fmt"

type User struct{
    Username string
    Sex string
    Age int
    AvatarUrl string
}

func NewUser(username, sex, AvatarUrl string,  age int) *User {
    user := &User{
	Username:  username,
	Sex:       sex,
	Age:       age,
	AvatarUrl: AvatarUrl,
    }
    return user
}
func main()  {
    // 使用构造函数生成一个
    user := NewUser("jack", "M", "https://www.person/14/profile/avat.png", 23)
    fmt.Println(*user)  // {jack M 23 https://www.person/14/profile/avat.png}
}

7.struct嵌套

  • 1.值类型嵌套
type Book struct {
    Name string
    Price float64
    author Author
}

type Author struct {
    Name string
    Age int
}

func main()  {
    book := &Book{
	Name: "天龙八部",
	Price: 39.8,
	author: struct {
            Name string
	    Age  int
	}{Name: "金庸", Age: 86},
        /* 也可以
        author: Author{
	    Name: "金庸",
	    Age:  86,
	},
        */
    }
    fmt.Println(*book)  // {天龙八部 39.8 {金庸 86}}
    fmt.Println((*book).author.Name)  // 金庸
}
  • 2.指针类型嵌套
type Book struct {
    Name string
    Price float64
    author *Author
}

type Author struct {
    Name string
    Age int
}

func main()  {
    book := &Book{
	Name: "天龙八部",
	Price: 39.8,
        author: *Author{
	    Name: "金庸",
	    Age:  86,
	},
    }
    fmt.Println(*book)  // {天龙八部 39.8 0xc0000044a0}
    fmt.Println((*book).author.Name)  // 金庸
    fmt.Println((*book.author).Name)  // 金庸
}

8.匿名字段

  • 1.匿名字段的定义和初始化
type User1 struct{
    Username string
    Sex string
    int
    string
}

func main()  {
    var user &User1
    user.Username = "lucy"
    user.Sex = "M"
    // 通过类型访问
    user.int = 22
    user.string = "https://www.person/5/profile/avat.png"
    fmt.Println(*user)  // {lucy M 22 https://www.person/5/profile/avat.png}
}
  • 2.匿名嵌套
type Book struct {
    Name string
    Price float64
    *Author
}

type Author struct {
    Name string
    Age int
}

func main()  {
    book := &Book{
        Name: "天龙八部",
        Price: 39.8,
        Author: &Author{
            Name: "金庸",
	    Age:  86,
        },
    }
    /*
    book := &Book{}
    book.Name = "天龙八部"
    book.Price = 39.8
    book.Author = &Author{
    	Name: "金庸",
    	Age:  86,
    }

    // 也可以使用下面方式初始化Author对象
    // book.Author = new(Author)
    // book.Age = 86  // 在book中未发现Age属性,则会去引用对象Author中寻找
    // book.Author.Name = "金庸"  // Name名称冲突,所以需要指定Author对象
    */
    fmt.Println(*book)  // {天龙八部 39.8 0xc0000044a0}
    fmt.Println((*book.Author).Name)  // 金庸
    fmt.Println((*book).Author.Name)  // 金庸
}
  • 3.匿名字段冲突解决
user := &User{}
user.Name = "lucy"
user.Age = "22"
user.Address = new(Address)
user.CreateTime = "2022-09-21"  // User下没有,然后去Address中寻找,最终赋值给Address对象中的CreateTime字段

user := &User{}
user.Name = "lucy"
user.Age = 22
user.Address = new(Address)
user.CreateTime = "2022-09-21"  // User有,直接初始化User对象中的该字段
// 如果想对Address中的CreateTime赋值,则必须指明对象为Address
user.Address.CreateTime = "2022-09-20"

user := &User{}
user.Name = "lucy"
user.Age = 22
user.Address = new(Address)
user.Email = new(Email)
user.CreateTime = "2022-09-21"  // 直接报错,User对象中没有CreateTime,Address和Email中都存在该字段,无法确定要对哪个对象中的CreateTime进行赋值。此时需要明确指定赋值对象
// 如果想对Address中的CreateTime赋值,则必须指明对象为Address
user.Address.CreateTime = "2022-09-20"
// 如果想对Email中的CreateTime赋值,则必须指明对象为Email
user.Email.CreateTime = "2022-09-19"

user := &User{}
user.Name = "lucy"
user.Age = 22
user.Address = new(Address)
user.Email = new(Email)
// User有,直接初始化User对象中的该字段
user.CreateTime = "2022-09-21" 
// 如果想对Address中的CreateTime赋值,则必须指明对象为Address
user.Address.CreateTime = "2022-09-20"
// 如果想对Email中的CreateTime赋值,则必须指明对象为Email
user.Email.CreateTime = "2022-09-19"

9.继承

  • go语言中的继承是通过结构体sturct中的匿名字段嵌套实现的
  • 嵌套多个struct时(类似于python中的多继承),如果嵌套对象都实现了同名方法,则需要显示指明调用哪个对象的方法(没有python中的MRO)
package main

import "fmt"

type Animal struct {
    Color string
    Name string
}

func (animal *Animal) Eat(foods string) {
    fmt.Printf("%s的%s在吃%s\n", animal.Color, animal.Name, foods)
}

func (animal *Animal) Sing(song string) {
    fmt.Printf("%s的%s在唱%s\n", animal.Color, animal.Name, song)
}

func (animal *Animal) Playing(toy string) {
    fmt.Printf("%s的%s在玩%s\n", animal.Color, animal.Name, toy)
}

type Dog struct {
    *Animal
}

func (dog *Dog) Playing(toy string) {
    // dog本身的方法
    fmt.Printf("%s的%s在自己家玩%s\n", dog.Color, dog.Name, toy)
}

type Cat struct {
    *Animal
}

func main()  {
    dog:=&Dog{}
    dog.Animal = new(Animal)
    // 以下属性和方法都是从Animal继承而来
    dog.Color = "棕色"
    dog.Name = "小狗tony"
    dog.Eat("骨头")  // 棕色的小狗tony在吃骨头
    dog.Playing("飞盘")  // 棕色的小狗tony在自己家玩飞盘
    dog.Animal.Playing("飞盘")  // 棕色的小狗tony在玩飞盘
    dog.Sing("生日快乐")  // 棕色的小狗tony在唱生日快乐
}

10.struct私有属性

  • 字段可见性:结构体内字段明如果以小写开头,则表示私有,外部包不可见

1.定义一个包文件夹structure,内部文件struct.go内容如下

// structure/struct.go
package structure

type User struct{
    Username string
    Sex string
    age int
}

2.structure同级创建一个main.go文件,内容如下

package main

import (
    // 包重命名 类似与python导包中的as  import pandas as pd
    userStruct "dcpuffer/dcpuffer/structure"
    "fmt"
)


func main()  {	
    user := &userStruct.User{}
    user.Username = "lucy"
    user.Sex = "lucy"
    user.age = 22  // 私有属性age不允许外部访问,此处会报错
    fmt.Println(user)
}

11.tag应用

  • tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来
package main

import (
    "encoding/json"
    "fmt"
)

// 如果结构体中字段为小写,则json后为空,因为对于json包,小写字段为私有属性,无法访问
type User struct {
    UserName string `json:"username"`  // 重点强调 :两侧不能有空格
    Sex string `json:"sex"`
    Score float64
}

func main()  {
    user := &User{
	UserName: "user01",
	Sex: "F",
	Score: 87.6,
    }
    fmt.Printf("%#v\n", *user)  // main.User{UserName:"user01", Sex:"F", Score:87.6}

    data, err := json.Marshal(user)
    if err == nil {
	fmt.Println(string(data))  // {"username":"user01","sex":"F","Score":87.6}
    } else {
	fmt.Println(err)
    }
}

12.结构体于json序列化

  • 序列化:结构体转成json数据格式
package main

import (
    "encoding/json"
    "fmt"
)

type Class struct {
    Name string
    Count int
    Stu []*Stu
}

type Stu struct {
    Name string
    Age int    
    Sex int
}

func main() {
    c := &Class{
	Name:  "一班",
	Count: 0,
	Stu:   nil,
    }
    for i := 0; i < 10; i++ {
	// 生成10个学生信息
	stu := &Stu{
	    //Name: "stu" + string(i),
	    Name: fmt.Sprintf("stu%d", i),
	    Age:  20,
	    Sex:  0,
	}
	c.Stu = append(c.Stu, stu)
    }
    c.Count = 10
    fmt.Println(*c)  // {一班 10 [0xc000064440 0xc000064460 ....]}
    data, err := json.Marshal(c)
    if err != nil {
	fmt.Println("json serialize failed")
    } else {
	fmt.Println(string(data))  // json串  {"Name":"一班","Count":10,"Stu":[{"Name":"stu0","Age":20,"Sex":0}, ...]}
    }
}
  • 反序列化:json转成结构体格式数据
var rawJson = `{"Name":"一班","Count":10,"Stu":[{"Name":"stu0","Age":20,"Sex":0}, ...]}`
c1 := &Class{}
err = json.Unmarshal([]byte(rawJson), c1)
if err != nil {
    fmt.Println("json unserialize failed")
} else {
    fmt.Println(*c1)  // {一班 10 [0xc000064440 0xc000064460 ....]}
    fmt.Println((*c1).Stu[0].Name)  // stu0
}

13.练习

  • 实现一个简单的学生管理系统,每个学生有分数、年级、性别、名字等字段,用户可以在控制台添加学生,修改学生信息,打印学生列表的功能
  • stumanger\struct.go
package main

import "fmt"

// 定义学生对象结构体
type Student struct {
    Username string
    Score float64
    Grade string
    Sex int
}

// 定义一个结构体类型的map,存储学生信息,键为学生姓名
var allStudents = make(map[string]*Student, 10)

// 为结构体添加AddOrUpdate方法
func (st *Student) AddOrUpdate() {
    stu, ok := allStudents[st.Username]
    if ok {
	// 更新
	stu.Score = st.Score
	stu.Sex = st.Sex
	stu.Grade = st.Grade
	fmt.Printf("学生[%s]信息修改成功!\n", st.Username)
    } else {
	// 新增
	allStudents[st.Username] = st
	fmt.Printf("学生[%s]信息添加成功!\n", st.Username)
    }
    return
}
  • stumanger\amin.go
package main

import "fmt"

func showMenu()  {
    fmt.Println("-----------------学生信息管理系统-----------------")
    fmt.Println("|               1.新增修改学生信息              |")
    fmt.Println("|               2.删除学生信息                  |")
    fmt.Println("|               3.查看学生信息                  |")
    fmt.Println("|               4.查看学生列表                  |")
    fmt.Println("------------------------------------------------")
    fmt.Print(">>>")
}

func aUStudent()  {
    var (
	username string
	sex int
	grade string
	score float64
    )
    fmt.Print("请输入姓名:")
    fmt.Scanf("%s\r", &username)
    fmt.Print("请输入性别(0代表女,1代表男):")
    fmt.Scanf("%d\r", &sex)
    fmt.Print("请输入年级:")
    fmt.Scanf("%s\r", &grade)
    fmt.Print("请输入分数:")
    fmt.Scanf("%f\r", &score)
    stu := &Student{username, score, grade, sex}
    stu.AddOrUpdate()
}

func getStudent(name string) {
    stu, ok := allStudents[name]
    if ok {
	fmt.Println("****************************")
	fmt.Println("* 姓名   性别   年级   分数 *")
	fmt.Printf("* %s   %d   %s   %.2f *\n", stu.Username, stu.Sex, stu.Grade,stu.Score)
	fmt.Println("****************************")
    } else {
        fmt.Printf("学生[%s]信息不存在\n", name)
    }
}

func listStudents() []Student {
    var students []Student
    for _, stus := range allStudents{
	students = append(students, *stus)
    }
    return students
}

func deleteStudent(name string)  {
    _, ok := allStudents[name]
    if ok {
	delete(allStudents, name)
	fmt.Printf("学生[%s]信息删除成功", name)
    } else {
	fmt.Printf("学生[%s]信息不存在\n", name)
    }
}

func main()  {
    for {
	showMenu()
	var sel int
	var username string
	fmt.Scanf("%d\n", &sel)
	switch sel {
	case 1:
	    aUStudent()
	case 2:
	    fmt.Print("请输入学生姓名:")
	    fmt.Scanf("%s\r", &username)
	    deleteStudent(username)
	case 3:
	    fmt.Print("请输入学生姓名:")
	    fmt.Scanf("%s\r", &username)
	    getStudent(username)
	case 4:
	    fmt.Println(listStudents())
	}
	fmt.Println(">>>")
    }
}

posted @ 2022-09-21 18:38  fatpuffer  阅读(102)  评论(0)    收藏  举报