Go中的struct
Posted 关灯吃面
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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) }
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() }
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) }
匿名字段
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 }
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()) }
model
package model func (s *School) GetName() string { return s.Name } func (s *School) GetAddr() string { return s.Addr }
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) }
7、组合和匿名字段
如果一个struct嵌套了另 一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。
如果一个struct嵌套了另 一个有名结构体,那么这个模式就叫组合。
8、多重继承
如果一个struct嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现了多重继承。
以上是关于Go中的struct的主要内容,如果未能解决你的问题,请参考以下文章