zoukankan      html  css  js  c++  java
  • Go中的struct

    1. 用来自定义复杂数据结构
    2.struct 里面可以包含多个字段(属性),字段可以是任意类型
    3.struct类型可以定义方法,注意和函数的区分
    4.struct类型是值类型
    5.struct类型可以嵌套
    6.Go语 言没有class类型,只有struct类型

    1. struct 声明:

    type 标识符 struct {
        field1 type
        field2 type
    }
    //例子:
    type Student struct {
        Name string
        Age int
        Score int
    }

    2.struct 中字段访问:和其他语言一样,使用点

    例子:
      var stu Student
      stu.Name = “tony”
      stu.Age = 18
      stu.Score=20
      fmt.Printf(“name=%s age=%d score=%d”, stu.Name, stu.Age, stu.Score)

    package main
    
    import(
        "fmt"
    )
    
    type Test struct {
        A int
        b int
    }
    
    type Student struct {
        Age int 
        Name string
        Score int
        Sex string
        a Test  //struct类型可以嵌套    
        t Int    
        c *int
    }
    
    func testStruct () {
        var s Student
        s.Age = 18
        s.Name = "tom"
        s.Score = 80
        s.Sex = "man"
        s.a.A = 100
        s.c = new(int)   //给指针分配内存
        *(s.c) = 100     //给指针对应的变量赋值
    
        fmt.Printf("name:%s age:%d score:%d sex:%s c=%d
    ", s.Name, s.Age, s.Score, s.Sex, *(s.c))
        fmt.Printf("%+v
    ", s)
        s1 := s        //结构体是值类型
        s1.Name = "jim"
        *(s1.c) = 200  //结构体内的指针还是引用类型
    
        fmt.Printf("name:%s age:%d score:%d sex:%s c=%d
    ", s.Name, s.Age, s.Score, s.Sex, *(s.c))
        fmt.Printf("%+v
    ", s)
        
    }
    
    func main() {    
        testStruct() 
    }

    3.struct定义的三种形式:

      a.var stu Student
      b.var stu *Student = new (Student)   //分配内存
      c.var stu *Student = &Student{}  //分配内存

      1)其中b和c返回的都是指向结构体的指针,访问形式如下:
      a. stu.Name、stu.Age和stu.Score或者 (*stu).Name、(*stu).Age等

    package main
    
    import(
        "fmt"
    )
    
    type Student struct {    
        Score int    
    }
    
    func testStruct () {
        
        var p3 = new(Student)
        (*p3).Score = 100
        p4 := p3    
        p4.Score = 1000
        //语法糖,底层会转成 (*p4).Score=1000的形式
        fmt.Printf("p3=%+v
    ", *p3)
    }
    
    func main() {    
        testStruct() 
    }

    4.struct的内存布局:struct中的所有字段在内存是连续的,布局如下:

    package main
    
    import(
        "fmt"
    )
    
    type Point struct {
        x int
        y int
    }
    
    type Rect struct {
        p1 Point
        p2 Point
    }
    
    
    
    type RectA struct {
        p1 *Point
        p2 *Point
    }
    
    func main(){
        var r1 Rect
        var r2 RectA
    
        r2.p1 = new(Point)
        var r3 = new(Point)
        var r4 = new(Point)
        r2.p2 = new(Point)
    
        fmt.Println(r3, r4)
        //r1的内存布局
        fmt.Printf("p1.x addr:%p
    ", &r1.p1.x)
        fmt.Printf("p1.y addr:%p
    ", &r1.p1.y)
        fmt.Printf("p2.x addr:%p
    ", &r1.p2.x)
        fmt.Printf("p2.y addr:%p
    ", &r1.p2.y)
        fmt.Println()
        fmt.Println()
        //r2的内存布局
        fmt.Printf("p1.x addr:%p
    ", &(r2.p1.x))
        fmt.Printf("p1.y addr:%p
    ", &(r2.p1.y))
        fmt.Printf("p2.x addr:%p
    ", &(r2.p2.x))
        fmt.Printf("p2.y addr:%p
    ", &(r2.p2.y))
        fmt.Printf("p1:%p
    ", &r2.p1)
        fmt.Printf("P2:%p
    ", &r2.p2)
    }
    View Code

     5、链表定义

    type Student struct {
        Name string
        Next* Student
    }

    每个节点包含下一个节点的地址,这样把所有的节点串起来了,通常把链表中的第一个节点叫做链表头。

    package main
    
    import(
        "fmt"
    )
    
    type People struct {
        Age int
        Name string
        Next *Student
    }
    
    type Student struct {
        Age int
        Name string
        Next *People
    }
    
    func testList(){
        var s Student   //给Student结构体分配内存
        s.Age = 100
        s.Name = "abc"
        s.Next = new(People) //给People结构体分配内存,s.Next是指针
        /*s.Next = &People{  //给People结构体分配内存
            Age:100,
            Name:"efg",
        }*/
        s.Next.Age = 1000    //s.Next.Age和(*(s.Next)).Age相同,go的语法糖
        s.Next.Name = "efg"
        s.Next.Next = new(Student)
        s.Next.Next.Age = 100
        s.Next.Next.Name = "999"
    
        fmt.Printf("s:%+v
    ", s)
        fmt.Printf("next:%v
    ", *(s.Next))
        fmt.Printf("people.next:%#v
    ", *(s.Next.Next))
    
        fmt.Printf("list header:%#v
    ", s)        
        fmt.Printf("data:%#v
    ", *(s.Next))
        fmt.Printf("data:%#v
    ", *(s.Next.Next))
        fmt.Printf("data:%#v
    ", s.Next.Next.Next)
        
    }
    
    func main(){
        testList()
    }
    View Code
    package main
    
    import(
        "fmt"
    )
    
    type Teacher struct {
        Name string
        Age int
        Next *Teacher
    }
    
    func NewTeacher(name string, age int) *Teacher {
        p := new(Teacher)
        p.Name = name
        p.Age = age
        return p
    }
    
    func createList() {
        var header *Teacher = &Teacher{}
        header.Age = 200
        header.Name = "a"
    
        fmt.Println("第一次打印")
        printList(header)
        
        /*p := new(Teacher)
        p.Name = "b"
        p.Age = 100*/  //同下一行
        p := NewTeacher("b", 100)
        header.Next = p
    
        fmt.Println("第二次打印")
        printList(header)
    
        p = new(Teacher)
        p.Name = "c"
        p.Age = 100
    
        header.Next.Next = p
    
        fmt.Println("第三次打印")
        printList(header)
    }
    
    func main(){    
        createList()    
    }
    链表构建例子
    package main
    
    import(
        "fmt"
    )
    
    type Teacher struct {
        Name string
        Age int
        Next *Teacher
    }
    
    func printList(h *Teacher) {
        for h != nil {
            fmt.Printf("Name:%v Age:%v
    ", h.Name, h.Age)
            h = h.Next
        }
    }
    
    func createInHeader(h *Teacher, name string, age int) (*Teacher) {
        p := &Teacher{}
        p.Age = age
        p.Name = name
    
        p.Next = h
        return p
    }
    
    //头插法,从链表最后一个开始往第一个节点开始建立
    func testCreateInHeader() {
        var header *Teacher
        header = createInHeader(header, "a", 18)
        header = createInHeader(header, "b", 19)
        header = createInHeader(header, "c", 20)    
        printList(header)
    }
    
    func main(){    
        testCreateInHeader()
    }
    头插法建立链表
    package main
    
    import(
        "fmt"
    )
    
    type Teacher struct {
        Name string
        Age int
        Next *Teacher
    }
    
    func printList(h *Teacher) {
        for h != nil {
            fmt.Printf("Name:%v Age:%v
    ", h.Name, h.Age)
            h = h.Next
        }
    }
    
    func createInTail(tail *Teacher, name string, age int) (*Teacher) {
        p := &Teacher{}
        p.Age = age
        p.Name = name
    
        if tail == nil {
            return p
        }
        tail.Next = p
        return p
    }
    
    func testCreateInTail() {
        var header *Teacher
        var tail *Teacher = header
    
        tail = createInTail(tail, "a", 18)
        if header == nil {   //建立第一个节点
            header = tail
        }
    
        tail = createInTail(tail, "b", 19)
        tail = createInTail(tail, "c", 20)
        tail = createInTail(tail, "a", 18)
        tail = createInTail(tail, "b", 19)
        tail = createInTail(tail, "c", 20)
    
        printList(header)
    }
    
    func main(){    
        testCreateInTail()
    }
    尾插法建立链表

     6、双链表定义

    type Student struct {
        Name string
        Next* Student
        Prev* Student
    }

    如果有两个指针分别指向前一个节点和后一个节点,我们叫做双链表。 

    7、二叉树定义

    type Student struct {
        Name string
        left* Student
        right* Student
    }

    如果每个节点有两个指针分别用来指向左子树和右子树,我们把这样的结构叫做二叉树

    8、结构体是用户单独定义的类型,不能和其他类型进行强制转换

    9、golang中的struct没有构造函数, 一般可以使用工厂模式来解决这个问题

    Package main
    
    type student struct {
        Name stirng
        Age int
    }
    
    
    func NewStudent(name string, age int) *student {     
        return &student{
            Name:name,
            Age:age,
        }
    }
    
    func main(){
        S := new (student)
        S := model.NewStudent(“tony”, 20)    
    }

    10、再次强调:

    1.make 用来分配map、slice、channel类型的内存
    
    2.new 用来分配值类型的内存

    struct中的tag

    我们可以为struct中的每个字段,写上 一个tag。这个tag可以通过反射的机制获取到,最常用的场景就是json序列化和反序列化

    package main
    
    import (
        "fmt"
        "encoding/json"
    )
    
    type Student struct {
        Name string `json:"name"`
        Age  int  `json:"age"`
        Sex string `json:"sex"`
    }
    
    func main() {
        var s Student
        s.Age = 200
        s.Name = "abc"
        s.Sex = "man"
    
        data, err := json.Marshal(s)  //序列化
        if err != nil {
            fmt.Printf("json marshal failed, err:%v", err)
            return
        }
    
        fmt.Printf("json data:%s
    ", data)
    
        var s1 Student 
        err = json.Unmarshal(data, &s1)  //反序列化
        if err != nil {
            fmt.Printf("json Unmarshal failed, err:%v", err)
            return
        }
        fmt.Printf("s1:%#v
    ", s1)
    }
    View Code

     匿名字段

    1、结构体中字段可以没有名字,即匿名字段

    type Car struct {
        Name stirng
        Age int
    }
    
    
    type Train struct {
        Car
        Start time.Time
        int
    }

    2、匿名字段冲突处理,先在子类字段赋值,然后在父类字段赋值(如果子类字段和父类字段有相同的,给父类字段赋值,使用上匿名字段即可)

    type Car struct {
        Name string
        Age int
    }
    type Train struct {
        Car
        Start time.Time
        Age int
    }
    type A struct {
        a int
    }
    type B struct {
        a int
        b int
    }
    type C struct {
        A
        B
    }

     

    package main
    
    import(
        "fmt"
    )
    
    type People struct {
        Name string
        Age int
    }
    
    type Student struct{
        Score int
        People   //匿名字段
        Name string
        int        //匿名字段
    }
    
    func main() {
        var s Student
        //s.People.Name = "abc"
        //s.People.Age = 100
        s.Name = "abc"     //子类字段赋值
        s.People.Name = "cdg"
        s.Age = 100        //子类字段没有Age,父类字段赋值
        s.Score = 100      //子类字段赋值
        s.int=100          s.int=100
    
        fmt.Printf("%#v
    ", s,s.int)
    }
    匿名字段使用

    方法

    1、Golang中的任何自定义类型,都可以有方法, 而不仅是struct 

      定义:func (recevier type) methodName(参数列表)(返回值列表){}

    package main
    
    import (
        "fmt"
    )
    
    type Int int
    
    func Add(a, b int) int{
        return a + b
    }
    
    func (i *Int)Add(a, b int)  {
        *i =  Int(a + b)
        return 
    }
    
    func (i Int)Sub(a, b int)  {
        i =  Int(a - b)
        return 
    }
    
    func main() {
        //c := Add(100, 300)
        //fmt.Println(c)   //300
    
        var a Int
        a.Add(100, 200)  //300,语法糖(&a).Add(100, 200)
        fmt.Println(a)
        a.Sub(100, 200)  
        fmt.Println(a)     //300
    }
    调用方法实例

    2、方法的调用

    func (this A) test() {
        fmt.Println(this.a)
    }
    
    
    var t A
    t.test()
    package main
    
    import (
        "fmt"
    )
    
    type Student struct {
        Name string
        Age int
    }
    
    func (s *Student) Set(name string, age int) {
        s.Name = name
        s.Age = age
    }
    
    func main() {
        var s Student
        s.Set("abc", 100)
        fmt.Println(s)   //abc  100
    }
    View Code

    3、方法和函数的区别

      方法是作用在特定类型上,函数可以随时调用。

    4、指针receiver  vs receiver

      本质上和函数的值传递和地址传递是一样的

    5、方法的访问控制,通过大小写控制

    mian

    package main
    
    import (
        "fmt"
        "code.oldboy.com/day5/model"
    )
    
    func main() {
        school := model.NewSchool("北京大学", "北京海淀区")
        fmt.Printf("school name is:%s
    ", school.GetName())
        fmt.Printf("school addr is:%s
    ", school.GetAddr())
    }
    View Code

    model

    package model
    
    func (s *School) GetName() string {
        return s.Name
    }
    
    func (s *School) GetAddr() string {
        return s.Addr
    }
    View Code

    6、继承

      如果 一个struct嵌套了另 一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。

    package main
    
    
    import(
        "fmt"
    )
    
    type People struct {
        Name string
        Age int
    }
    
    type Student struct{
        Score int
        People   //匿名字段
        Name string
        int        //匿名字段
    }
    
    func (p *People) Format() string {
        return fmt.Sprintf("name=%s&age=%d", p.Name, p.Age)
    }
    
    func (p *Student) Format() string {
        return fmt.Sprintf("name=%s&age=%d", p.Name, p.Age)
    }
    
    func main() {
        var s Student
        s.Age = 200
        s.People.Name = "abc"
        //ret := s.Format()  //调用子类的方法
        ret := s.People.Format()
        fmt.Println("format result:", ret)
    }
    View Code

    7、组合和匿名字段

      如果一个struct嵌套了另 一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。

      如果一个struct嵌套了另 一个有名结构体,那么这个模式就叫组合。

    8、多重继承

      如果一个struct嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现了多重继承。

  • 相关阅读:
    文件操作fopen
    随机数应用
    随机数
    通过命令行参数打开文件
    命令行参数
    内存分区
    const修饰指针+volatile +restrict
    类型限定词——const
    (app笔记)Appium如何开启、配置、启用
    (app笔记)Memory Fill内存填充
  • 原文地址:https://www.cnblogs.com/domestique/p/8229021.html
Copyright © 2011-2022 走看看