go中的struct

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go中的struct相关的知识,希望对你有一定的参考价值。

参考技术A go和其他语言一样,可以申明新的“结构体”,struct可以作为其他类型的属性活字段的容器,定义方式如下:

type 关键字申明 person 为 struct 类型,person包含 name 属性和 age 属性,对应的类型为 string 和 int 。我们看看如何使用struct:

除了上面使用的方式使用 struct ,还有三种方式可以使用申明struct类型

完整是使用struct 例子

当我们创建结构体时,字段可以只有类型,而没有字段名。这样的字段称为匿名字段(Anonymous Field)。习惯上匿名字段叫内嵌,具名字段叫组合

上面我们申明了两个结构体 Student 和 Person ,其中 Student 组合了 Person 字段,那么 Student 包含了 Person 中的所有字段

从上面的例子可以看出来,struct 不仅可以将struct作为匿名字段,自定义类型、内置类型都可以作为匿名字段,也可以进行相应的函数操作。

这里我们有个问题,Person上有一个name属性,如果Student上也有一个name属性,那么我们怎么办呢?其实在go里面,最外层的属性具有有限的访问权限,当你通过Student.name访问的时候是访问Student上的属性。同理,我们可以通过Student.Person访问Person上的属性,如:

如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,那样的话两个结构体将可以使用 == 或 != 运算符进行比较,但不支持 > 或 < 。

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\\n", s.Name, s.Age, s.Score, s.Sex, *(s.c))
    fmt.Printf("%+v\\n", s)
    s1 := s        //结构体是值类型
    s1.Name = "jim"
    *(s1.c) = 200  //结构体内的指针还是引用类型

    fmt.Printf("name:%s age:%d score:%d sex:%s c=%d\\n", s.Name, s.Age, s.Score, s.Sex, *(s.c))
    fmt.Printf("%+v\\n", 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\\n", *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\\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()
    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.y addr:%p\\n", &(r2.p2.y))
    fmt.Printf("p1:%p\\n", &r2.p1)
    fmt.Printf("P2:%p\\n", &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\\n", s)
    fmt.Printf("next:%v\\n", *(s.Next))
    fmt.Printf("people.next:%#v\\n", *(s.Next.Next))

    fmt.Printf("list header:%#v\\n", s)        
    fmt.Printf("data:%#v\\n", *(s.Next))
    fmt.Printf("data:%#v\\n", *(s.Next.Next))
    fmt.Printf("data:%#v\\n", 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\\n", 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\\n", 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\\n", 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\\n", 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\\n", 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\\n", school.GetName())
    fmt.Printf("school addr is:%s\\n", 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嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现了多重继承。

 

以上是关于go中的struct的主要内容,如果未能解决你的问题,请参考以下文章

Golang关于Go中的struct{}

golang go中的样本整数struct容器

Go语言中的struct的初始化。

Go中的struct之方法method初体验

Go中的"类"之struct的初体验

浅谈Go语言中的结构体struct & 接口Interface & 反射