Day5.struct链表匿名字段

一.结构体

(一).结构体要点 基础部分略

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

1.struct里指针属性使用指南

package main

import "fmt"

type testPoint struct {
    c *int
}

func main() {
    var s testPoint
    s.c = new(int)
    *(s.c) = 100
    fmt.Printf("%+v,%d", s, *(s.c))
}

2.struct定义的三种形式

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

b and c返回的都是指向指针结构体的指针。访问形式如下:
stu.Name / (*stu).Name

3.struct内存布局

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

main.go

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 = &Point{}
    r2.p2 = &Point{}

    // r1 内存布局
    fmt.Printf("p1.x addr:%p\n", &r1.p1.x)
    fmt.Printf("p1.y addr:%p\n", &r1.p1.y)
    fmt.Printf("p2.x addr:%p\n", &r1.p2.x)
    fmt.Printf("p2.y addr:%p\n", &r1.p2.y)

    fmt.Println()

    // r2内存布局
    fmt.Printf("p1.x addr:%p\n", &r2.p1.x)
    fmt.Printf("p1.y addr:%p\n", &r2.p1.y)
    fmt.Printf("p2.x addr:%p\n", &r2.p2.x)
    fmt.Printf("p2.x addr:%p\n", &r2.p2.y)
}
PS D:\project> .\bin\rect.exe
p1.x addr:0xc00000e400
p1.y addr:0xc00000e408
p2.x addr:0xc00000e410
p2.y addr:0xc00000e418

p1.x addr:0xc000010090
p1.y addr:0xc000010098
p2.x addr:0xc0000100a0
p2.x addr:0xc0000100a8

4.golang中工厂模式

(1).golang中的struct没有构造函数,一般可以使用工厂模式来解决问题

model/model.go 构造函数例子

package model

type School struct {
    Name string
    Addr string
}

func NewSchool(name, addr string) *School {
    return &School{
        Name: name,
        Addr: addr,
    }
}

model_test/main.go

package main

import (
    "fmt"
    "godev2/day5/model"
)

func main() {
    school := model.NewSchool("北京大学", "海淀校区")
    fmt.Println(school)
}

二.链表

(一).链表定义

每个节点包含下一个节点的地址,这样把所有的节点串起来了,通常把链表中的第一个节点叫做链表头。
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
    s.Age = 100
    s.Name = "jesse1"
    s.Next = &People{
        Age:  200,
        Name: "maggie2",
    }
    s.Next.Next = &Student{}
    s.Next.Next.Age = 300
    s.Next.Next.Name = "jesse3"

    fmt.Printf("s:%+v\n", s)
    fmt.Printf("next:%+v\n", s.Next)

    fmt.Printf("list header:%#v\n", s)

    fmt.Printf("data:%#v\n", s.Next)
    fmt.Printf("data:%#v\n", s.Next.Next)

    /* 如果链表指向的数据不存在 则会直接显示为nil */
    fmt.Printf("data:%#v\n", s.Next.Next.Next)

}

func main() {
    testList()
}
PS D:\project> go install D:\project\src\godev2\day5\list\  
PS D:\project> .\bin\list.exe  
s:{Age:100 Name:jesse1 Next:0xc00004c420}  
next:&{Age:200 Name:maggie2 Next:0xc00004c440}  
list header:main.Student{Age:100, Name:"jesse1", Next:  (*main.People)(0xc00004c420)}  
data:&main.People{Age:200, Name:"maggie2", Next:(*main.Student)(0xc00004c440)}  
data:&main.Student{Age:300, Name:"jesse3", Next:(*main.People)(nil)}  
data:(*main.People)(nil)

(二).链表头部插入案例

main.go

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
    s.Age = 100
    s.Name = "jesse1"
    s.Next = &People{
        Age:  200,
        Name: "maggie2",
    }
    s.Next.Next = &Student{}
    s.Next.Next.Age = 300
    s.Next.Next.Name = "jesse3"

    fmt.Printf("s:%+v\n", s)
    fmt.Printf("s.next:%v\n", *(s.Next))

    fmt.Printf("list header:%#v\n", s)

    fmt.Printf("people.next:%#v\n", s.Next)
    fmt.Printf("data:%#v\n", s.Next.Next)
    fmt.Printf("data:%#v\n", s.Next.Next.Next)

}

func main() {
    //testList()
    //createList()
    testCreateInHeader()
}

list.go

package main

import "fmt"

type Teacher struct {
    Name string
    Age  int
    Next *Teacher
}

func createList() {
    var header *Teacher = &Teacher{}
    header.Age = 400
    header.Name = "sky4"
    printList(header)

    fmt.Println("第一次打印")
    p := &Teacher{}

    p.Age = 500
    p.Name = "sky5"
    header.Next = p
    fmt.Println("第二次打印")
    printList(header)

    p = &Teacher{}

    p.Age = 600
    p.Name = "sky6"
    header.Next.Next = p
    fmt.Println("第3次打印")
    printList(header)

}

func createInHeader(h *Teacher, name string, age int) *Teacher {
    p := &Teacher{}
    p.Age = age
    p.Name = name
    p.Next = h
    return p

}

func printList(h *Teacher) {
    for h != nil {
        fmt.Printf("Name:%v age:%v\n", h.Name, h.Age)
        h = h.Next
    }
}

func testCreateInHeader() {
    var header *Teacher
    header = createInHeader(header, "a", 18)
    header = createInHeader(header, "b", 19)
    header = createInHeader(header, "c", 20)
    header = createInHeader(header, "d", 21)
    printList(header)
}

(三).链表尾部插入案例(继承二部分代码)

// INSERT END
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, "d", 21)
    printList(header)
}

(四).双链表定义 二叉树定义

1.如果有两个指针分别指向前一个节点和后一个节点,我们叫做双链表。
2.如果每个节点都有两个指针分别用来指向左子树和右子树,我们把这样的结构叫做二叉树。

type Stu struct {
    Name string
    left* Stu
    right* Stu
}
3.make用来分配map/slice/channel类型的内存。
4.new用来分配值类型的内存。

(五).struct tag

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

type student struct {
    Name string `json="name"`
    Age int `json="age"`
}

json序列化和反序列化例子

package main

import (
    "encoding/json"
    "fmt"
)

type Student struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    Sex  string `json:"sex"`
}

func main() {
    var s Student
    s.Name = "jesse"
    s.Age = 99

    /*
        s.sex = "man"
        json里面无法访问到结构体内部成员变量
        json data:{"Name":"jesse","Age":99}
    */
    s.Sex = "man"

    result, err := json.Marshal(s)
    if err != nil {
        fmt.Printf("json marshal failed,err %v", err)
    }

    fmt.Printf("json data:%s\n", result)


    // 反序列化例子
    var s1 Student

    err = json.Unmarshal(result, &s1)
    if err != nil {
        fmt.Printf("json unmarshal failed,err %v", err)
        return
    }
    fmt.Printf("s1:%#v\n", s1)
}

(六).struct匿名字段

package main

import "fmt"

type People struct {
    Name string
    Age  int
}

type Student struct {
    Score int
    People
    int
    Name string
}

func main() {
    var s Student
    s.Name = "jesse"
    s.Age = 18
    s.int = 1000000

    s.People.Name = "peoplename"
    s.Name = "studentname"

    fmt.Printf("%#v, int:%d,%s", s, s.int, s.Name)
}

三.方法

(一).golang中的任何自定义类型 都可以有方法,而不仅仅是struct

// func (receiver type) methodName(参数列表)(返回值列表){}
// 简单示例:

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("jesse", 100)
    fmt.Println(s)
}

(二).方法的访问控制 通过大小写控制

// 可以被外部调用
func (s *School) GetName() string {
    return s.Name
}

// 不可用被外部调用
func (s *School) getAddr() string {
    return s.Addr
}
package main

import (
    "fmt"
    "godev2/day5/model"
)

func testMethod() {
    var s Student
    s.Set("jesse", 100)
    fmt.Println(s)
}

func testModel() {
    school := model.NewSchool("bei jing da xue", "bei jing hai dian")
    fmt.Printf("school name is %s\n", school.GetName())
}

func main() {
    testModel()
}

(三).继承

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

package main

import "fmt"

type People struct {
    Name string
    Age  int
}

// 如果子类和父类有同样的方法 则优先访问子类的方法。
func (p *People) Format() string {
    return fmt.Sprintf("father method: name=%s&age=%d", p.Name, p.Age)
}

func (p *Student) Format() string {
    return fmt.Sprintf("son method: name=%s&age=%d", p.Name, p.Age)
}

type Student struct {
    Score int
    People
    int
    Name string
}

func testMethod() {
    var s Student
    s.People.Name = "jesse1"
    s.Age = 19
    // 访问父类方法方式:ret := s.People.Format()
    ret := s.Format()
    fmt.Println("format result:", ret)
}

func test1() {
    var s Student
    s.Name = "jesse"
    s.Age = 18
    s.int = 1000000
    s.People.Name = "peoplename"
    s.Name = "studentname"
    fmt.Printf("%#v, int:%d,%s", s, s.int, s.Name)
}

func main() {
    testMethod()
}

(四).组合和匿名字段 多重继承

1.如果一个struct嵌套了另外一个匿名结构体,那么这个结构体就可以直接访问匿名结构体的方法,从而实现继承。
2.如果一个struct嵌套了另外一个有名结构体,那么这个模式就叫组合。
3.如果一个struct嵌套了多个匿名结构体,那么这个结构体可以直接访问多个匿名结构体的方法,从而实现了多重继承。 实际业务开发基本不会使用。

Copyright © zhangluya.com 2019            UPDATE 2019-09-18 17:53:04

results matching ""

    No results matching ""