结体体定义如下:

type author struct{

field1 type1

field2 type2

...

}

结构体的定义格式如下:

type 类型名 struct{

字段1 字段1类型

字段2 字段2类型

……

}

基本实例化格式如下:

var ins T

T为结构体类型。

ins为结构体的实例。

创建指针类型的结构体:

使用new的格式如下:

ins:=new(T)

T为类型,可以是结构体、整型、字符串等。

ins:T类型被实例化后保存到ins变量中,ins的类型为*T,属于指针。

代码如下:

package maintype Player struct {Name        stringHealthPoint intMagicPoint  int
}func main() {tank := new(Player)tank.Name = "Canon"tank.HealthPoint = 300
}

取结构体的地址实例化:

取地址格式如下:

ins := &T{}

T表示结构体类型。

ins为结构体的实例,类型为*T,是指针类型。

声明并创建一个简单的结构体,程序清单如下:

package mainimport("fmt"
)
type Movie struct{       //结构体Name stringRating float32
}
func main(){m:=Movie{Name:"Citizen Kane",Rating:10,}fmt.Println(m.Name,m.Rating)
}

运行结果如下:

Citizen Kane 10

声明一个类型为结构体的变量,程序清单如下:

package mainimport("fmt"
)
type Movie struct{        //结构体Name stringRating float32
}
func main(){var m Moviefmt.Printf("%+v\n",m)   //零值m.Name="Metropolis"m.Rating=0.9918fmt.Printf("%+v\n",m)
}

运行结果如下:

{Name: Rating:0}
{Name:Metropolis Rating:0.9918}

使用关键字new创建结构体实例,程序清单如下:

package mainimport("fmt"
)
type Movie struct{        //结构体Name stringRating float32
}
func main(){m:=new(Movie)m.Name="Metropolis"m.Rating=0.99fmt.Printf("%+v\n",m)
}

运行结果如下:

&{Name:Metropolis Rating:0.99}

代码如下:

package mainimport ("fmt"
)type myStruct struct {i1  intf1  float32str string
}func main() {ms := new(myStruct)ms.i1 = 10ms.f1 = 15.5ms.str = "Google"fmt.Printf("int: %d\n", ms.i1)fmt.Printf("float: %f\n", ms.f1)fmt.Printf("string: %s\n", ms.str)fmt.Println(ms)
}

运行结果如下:

int: 10
float: 15.500000
string: Google
&{10 15.5 Google}

使用简短变量赋值创建结构体实例,程序清单如下:

package mainimport("fmt"
)
type Movie struct{        //结构体Name stringRating float32
}
func main(){m:=Movie{Name:"Metropolis",Rating:0.99,}fmt.Printf("%+v\n",m)
}

运行结果如下:

{Name:Metropolis Rating:0.99}

使用简短变量赋值创建嵌套结构体实例,程序清单如下:

package mainimport("fmt"
)
type Superhero struct{Name stringAge intAddress Address
}
type Address struct{Number intStreet stringCity string
}
func main(){e:=Superhero{Name:"Batman",Age:32,Address:Address{Number:1007,Street:"Mountain Drive",City:"Gotham",},}fmt.Printf("%+v\n",e)fmt.Println(e.Address.Street)
}

运行结果如下:

{Name:Batman Age:32 Address:{Number:1007 Street:Mountain Drive City:Gotham}}
Mountain Drive

使用构造函数自定义默认值,程序清单如下:

package mainimport("fmt"
)
type Alarm struct{Time stringSound string
}
func NewAlarm(time string)Alarm{a:=Alarm{Time:time,Sound:"Klaxon",}return a
}
func main(){fmt.Printf("%+v\n",NewAlarm("07:00"))
}

运行结果如下:

{Time:07:00 Sound:Klaxon}

对两个数据字段值相等的结构体进行比较,程序清单如下:

package mainimport("fmt"
)
type Drink struct{Name stringIce bool
}
func main(){a:=Drink{Name:"Lemonade",Ice:true,}b:=Drink{Name:"Lemonade",Ice:true,}if a==b{fmt.Println("a and b are the same")}fmt.Printf("%+v\n",a)fmt.Printf("%+v\n",b)
}

运行结果如下:

a and b are the same
{Name:Lemonade Ice:true}
{Name:Lemonade Ice:true}

检查结构休的类型,程序清单如下:(没学过reflect)

package mainimport("fmt""reflect"
)
type Drink struct{Name stringIce bool
}
func main(){a:=Drink{Name:"Lemonade",Ice:true,}b:=Drink{Name:"Lemonade",Ice:true,}fmt.Println(reflect.TypeOf(a))fmt.Println(reflect.TypeOf(b))
}

运行结果如下:

main.Drink
main.Drink

以值引用的方式复制结构体,程序清单如下:

package mainimport("fmt"
)
type Drink struct{Name stringIce bool
}
func main(){a:=Drink{Name:"Lemonade",Ice:true,}b:=ab.Ice=falsefmt.Printf("%+v\n",b)fmt.Printf("%+v\n",a)fmt.Printf("%p\n",&a)fmt.Printf("%p\n",&b)
}

运行结果如下:

{Name:Lemonade Ice:false}
{Name:Lemonade Ice:true}
0xc00000c030
0xc00000c048

以指针引用的方式复制结构体,程序清单如下:

package mainimport("fmt"
)
type Drink struct{Name stringIce bool
}
func main(){a:=Drink{Name:"Lemonade",Ice:true,}b:=&ab.Ice=falsefmt.Printf("%+v\n",*b)fmt.Printf("%+v\n",a)fmt.Printf("%p\n",b)fmt.Printf("%p\n",&a)
}

运行结果如下:

{Name:Lemonade Ice:false}
{Name:Lemonade Ice:false}
0xc00000c030
0xc00000c030

使用三个不同的方式调用方法,代码如下:

package mainimport ("fmt""strings"
)type Person struct {firstName stringlastName  string
}func upPerson(p *Person) {p.firstName = strings.ToUpper(p.firstName)p.lastName = strings.ToUpper(p.lastName)
}
func main() {//作为值类型var pers1 Personpers1.firstName = "Zhang"pers1.lastName = "Sansan"upPerson(&pers1)fmt.Printf("the name is %s %s\n", pers1.firstName, pers1.lastName)//作为指针pers2 := new(Person)pers2.firstName = "Zhang"pers2.lastName = "Sansan"(*pers2).lastName = "Sansan" //这也是合法的upPerson(pers2)fmt.Printf("the name is %s %s\n", pers2.firstName, pers2.lastName)//作为字面量pers3 := &Person{"Zhang", "Sansan"}upPerson(pers3)fmt.Printf("the name is %s %s\n", pers3.firstName, pers3.lastName)
}

运行结果如下:

the name is ZHANG SANSAN
the name is ZHANG SANSAN
the name is ZHANG SANSAN

递归结构体:

可以通过引用自身来定义,这在定义链表或二叉树的元素(通常叫节点)时特别有用,此时节点包含指向临时节点的链接(地址)。

data字段用于存放有效数据,su指针指向后继节点:

type Node struct{

data float64

su *Node

}

链表中的第一个元素叫作head,它指向第二个元素;最后一个元素叫作tail,它没有后继元素,所以它的su值为nil。当然真实的链接会有很多数据节点,并且链表可以动态增长或收缩。

可以定义一个双向链表,它有一个前趋节点pr和一个后继节点su:

type Node struct{

pr *Node

data float64

su *Node

}

二叉树中每个节点最多能链接到两个节点:左节点(le)和右节点(ri),这两个节点本身又可以有左右节点。树的顶层节点叫作根节点(root),底层没有子节点的节点叫作叶子节点(leaves),叶子节点的le和ri指针为nil值。在Go语言中可以如下定义二叉树:

type Tree strcut{

le *Tree

data float64

ri *Tree

}

结构体使用

1、自定义包的结构体

main.go使用了一个结构体,它来自struct_pack下的包structPack:

package struck_packtype ExpStruct struct {Mi1 intMf1 float32
}

下面是main.go的代码:

package mainimport ("fmt""./struct_pack/struckPack"
)func main() {struct1:=new(structPack.Expstruct)struct1.Mi1=10struct1.Mf1=16.fmt.Prinf("Mi1 = %d\n",struct.Mi1)fmt.Prinf("Mf1 = %d\n",struct.Mf1)
}

编译执行输出:(编译失败)

Mi1=10

Mf1=16.000000

2、结构体成员访问

在Go语言中,访问结构体成员需要使用点号操作符,格式为“结构体.成员名”。

代码如下:

package mainimport ("fmt"
)type Employee struct {ID      intName    stringAddress stringPhone   string
}func main() {var employee1 Employeeemployee1.ID = 10001employee1.Name = "Tom"employee1.Address = "xxxx"employee1.Phone = "1881412xxxx"fmt.Printf("employee1 ID: %d\n", employee1.ID)fmt.Printf("employee1 Name: %s\n", employee1.Name)fmt.Printf("employee1 Address: %s\n", employee1.Address)fmt.Printf("employee1 Phone: %s\n", employee1.Phone)
}

运行结果如下:

employee1 ID: 10001
employee1 Name: Tom
employee1 Address: xxxx
employee1 Phone: 1881412xxxx

使用结构体指针重写以上实例,代码如下:

package mainimport ("fmt"
)type Employee struct {ID      intName    stringAddress stringPhone   string
}func printEmployee(employee *Employee) {fmt.Printf("Employee ID: %d\n", employee.ID)fmt.Printf("Employee Name: %s\n", employee.Name)fmt.Printf("Employee Address: %s\n", employee.Address)fmt.Printf("Employee Phone: %s\n", employee.Phone)
}func main() {var employee Employeeemployee.ID = 10001employee.Name = "Tom"employee.Address = "xxxx"employee.Phone = "1881412xxxx"printEmployee(&employee)
}

运行结果如下:

Employee ID: 10001
Employee Name: Tom
Employee Address: xxxx
Employee Phone: 1881412xxxx

3、结构体参数传输

代码如下:

package mainimport ("fmt"
)type Employee struct {ID      intName    stringAddress stringPhone   string
}//形式传参
func operateEmployee1(employee Employee) {employee.ID = 10010
}//指针传参
func operateEmployee2(employee *Employee) {employee.ID = 10010
}func main() {var employee Employeeemployee.ID = 10001employee.Name = "Tom"employee.Address = "xxxx"employee.Phone = "1881412xxxx"fmt.Printf("形式传参之前,employee ID : %d\n", employee.ID)operateEmployee1(employee)fmt.Printf("形式传参之后,employee ID : %d\n", employee.ID)fmt.Printf("指针传参之前,employee ID : %d\n", employee.ID)operateEmployee2(&employee)fmt.Printf("指针传参之后,employee ID : %d\n", employee.ID)
}

运行结果如下:

形式传参之前,employee ID : 10001
形式传参之后,employee ID : 10001
指针传参之前,employee ID : 10001
指针传参之后,employee ID : 10010

形式伟参中employee只是传递了一个副本到另一个函数,函数中操作的是副本,对employee没有任何影响;而在指针传参中employee传递的是地址,函数中的操作会影响到employee。

带标签的结构体

标签内容只有reflect包能获取。

代码如下:

package mainimport ("fmt""reflect"
)type TagType struct {field1 bool   "是否有存货"field2 string "商品名称"field3 int    "商品价格"
}func refTag(tt TagType, ix int) {ttType := reflect.TypeOf(tt)ixField := ttType.Field(ix)fmt.Printf("%v\n", ixField.Tag)
}
func main() {tt := TagType{true, "LPhone X", 1}for i := 0; i < 3; i++ {refTag(tt, i)}
}

运行结果如下:

是否有存货
商品名称
商品价格

匿名字段和内嵌结构体

1.匿名字段

代码如下:

package mainimport ("fmt"
)type firstS struct {in1 intin2 int
}
type secondS struct {b      intc      float32int    //匿名字段firstS //匿名字段
}func main() {sec := new(secondS)sec.b = 6sec.c = 7.5sec.int = 60sec.in1 = 5sec.in2 = 10fmt.Printf("sec.b is: %d\n", sec.b)fmt.Printf("sec.c is: %f\n", sec.c)fmt.Printf("sec.int is: %d\n", sec.int)fmt.Printf("sec.in1 is: %d\n", sec.in1)fmt.Printf("sec.in2 is: %d\n", sec.in2)//使用结构体字面量sec2 := secondS{6, 7.5, 60, firstS{5, 10}}fmt.Println("sec2 is:", sec2)
}

运行结果如下:

sec.b is: 6
sec.c is: 7.500000
sec.int is: 60
sec.in1 is: 5
sec.in2 is: 10
sec2 is: {6 7.5 60 {5 10}}

在一个结构体中对于每一个数据类型只能有一个匿名字段。

2.内嵌结构体

代码如下:

package mainimport ("fmt"
)type A struct {ax, ay int
}type B struct {Abx, by float32
}func main() {b := B{A{1, 2}, 3.0, 4.0}fmt.Println(b.ax, b.ay, b.bx, b.by)fmt.Println(b.A)
}

运行结果如下:

1 2 3 4
{1 2}

3.命名冲突

当两个字段拥有相同的名字时会产生冲突,一般情况下有两种:

(1)外层字段的名字覆盖内层字段的名字,但是两者的内存空间都会保留,这提供了一种重载字段或方法的方式。

(2)相同的名字在同层次结构体中出现了重复,并且这个字段被程序调用了,这将导致程序错误。这种情况只能由程序员自己修改。

类型系统

Go语言是一种静态类型的编程语言。

int64类型的值需要8B(64b),float32类型的值需4B(32b),bool类型的值需要1B(8b)。

代码如下:

package mainimport ("fmt""reflect"
)type Student struct {name string "学生名字"          // 结构体标签Age  int    "学生年龄"          // 结构体标签Room int    `json:"Roomid"` // 结构体标签
}func main() {st := Student{"Titan", 14, 102}fmt.Println(reflect.TypeOf(st).Field(0).Tag)fmt.Println(reflect.TypeOf(st).Field(1).Tag)fmt.Println(reflect.TypeOf(st).Field(2).Tag)
}

运行结果:

[Running] go run "c:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\tempCodeRunnerFile.go"
学生名字
学生年龄
json:"Roomid"[Done] exited with code=0 in 12.15 seconds

代码如下:

package mainimport ("fmt"
)type Writer interface {Write()
}type Author struct {name stringWriter
}// 定义新结构体,重点是实现接口方法Write()
type Other struct {i int
}func (a Author) Write() {fmt.Println(a.name, "  Write.")
}// 新结构体Other实现接口方法Write(),也就可以初始化时赋值给Writer 接口
func (o Other) Write() {fmt.Println(" Other Write.")
}func main() {//  方法一:Other{99}作为Writer 接口赋值Ao := Author{"Other", Other{99}}Ao.Write()// 方法二:简易做法,对接口使用零值,可以完成初始化Au := Author{name: "Hawking"}Au.Write()
}

运行结果如下:

[Running] go run "c:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\main.go"
Other   Write.
Hawking   Write.[Done] exited with code=0 in 26.289 seconds

代码如下 :

package mainimport ("fmt"
)type Human struct {name   string // 姓名Gender string // 性别Age    int    // 年龄string        // 匿名字段
}type Student struct {Human     // 匿名字段Room  int // 教室int       // 匿名字段
}func main() {//使用new方式stu := new(Student)stu.Room = 102stu.Human.name = "Titan"stu.Gender = "男"stu.Human.Age = 14stu.Human.string = "Student"fmt.Println("stu is:", stu)fmt.Printf("Student.Room is: %d\n", stu.Room)fmt.Printf("Student.int is: %d\n", stu.int)         // 初始化时已自动给予零值:0fmt.Printf("Student.Human.name is: %s\n", stu.name) //  (*stu).namefmt.Printf("Student.Human.Gender is: %s\n", stu.Gender)fmt.Printf("Student.Human.Age is: %d\n", stu.Age)fmt.Printf("Student.Human.string is: %s\n", stu.string)// 使用结构体字面量赋值stud := Student{Room: 102, Human: Human{"Hawking", "男", 14, "Monitor"}}fmt.Println("stud is:", stud)fmt.Printf("Student.Room is: %d\n", stud.Room)fmt.Printf("Student.int is: %d\n", stud.int) // 初始化时已自动给予零值:0fmt.Printf("Student.Human.name is: %s\n", stud.Human.name)fmt.Printf("Student.Human.Gender is: %s\n", stud.Human.Gender)fmt.Printf("Student.Human.Age is: %d\n", stud.Human.Age)fmt.Printf("Student.Human.string is: %s\n", stud.Human.string)
}

运行结果如下:

[Running] go run "c:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go\tempCodeRunnerFile.go"
stu is: &{{Titan 男 14 Student} 102 0}
Student.Room is: 102
Student.int is: 0
Student.Human.name is: Titan
Student.Human.Gender is: 男
Student.Human.Age is: 14
Student.Human.string is: Student
stud is: {{Hawking 男 14 Monitor} 102 0}
Student.Room is: 102
Student.int is: 0
Student.Human.name is: Hawking
Student.Human.Gender is: 男
Student.Human.Age is: 14
Student.Human.string is: Monitor[Done] exited with code=0 in 2.352 seconds

代码如下 :

运行结果如下:

代码如下 :

运行结果如下:

代码如下 :

运行结果如下:

方法

一、方法声明

1.方法的声明

定义方法的一般格式如下:

func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }

在方法名之前,func关键字之后的括号中指定接收者。

一个结构体上的简单方法的例子,代码如下:

package mainimport ("fmt"
)type TwoInts struct {a intb int
}func (tn *TwoInts) AddThem() int {return tn.a + tn.b
}func (tn *TwoInts) AddToParam(param int) int {return tn.a + tn.b + param
}func main() {two1 := new(TwoInts)two1.a = 12two1.b = 10fmt.Printf("和为:%d\n", two1.AddThem())fmt.Printf("将它们添加到参数:%d\n", two1.AddToParam(20))two2 := TwoInts{3, 4}fmt.Printf("和为:%d\n", two2.AddThem())
}

运行结果如下:

和为:22
将它们添加到参数:42
和为:7

非结构体类型方法的例子,代码如下:

package mainimport ("fmt"
)type IntVector []intfunc (v IntVector) Sum() (s int) {for _, x := range v {s += x}return
}func main() {fmt.Println(IntVector{1, 2, 3}.Sum())
}

运行结果如下:

6

代码如下:

package mainimport ("fmt""time"
)type myTime struct {time.Time //匿名字段
}func (t myTime) first3Chars() string {return t.Time.String()[0:3]
}func main() {m := myTime{time.Now()}fmt.Println("完整的时间格式:", m.String())fmt.Println("前三个字符:", m.first3Chars())
}

运行结果如下:

完整的时间格式: 2022-02-01 20:56:18.069698 +0800 CST m=+0.000139031
前三个字符: 202

2.函数和方法的区别

函数将变量作为参数:Function1(recv);方法在变量上被调用:recv.Method1()。

二、为类型添加方法

代码如下:

package mainimport ("fmt"
)type Integer intfunc (a Integer) Less(b Integer) bool {return a < b
}func main() {var a Integer = 1if a.Less(2) {fmt.Println(a, "Less 2")}
}

运行结果如下:

1 Less 2

代码如下:

package mainimport ("fmt"
)type Integer intfunc Integer_Less(a Integer, b Integer) bool {return a < b
}func main() {var a Integer = 1if Integer_Less(a, 2) {fmt.Println(a, "Less 2")}
}

运行结果如下:

1 Less 2

三、工厂方法创建结构体

四、基于指针对象的方法

代码如下:

package mainimport ("fmt"
)type HttpResponse struct{ status_code int }func (r *HttpResponse) validResponse() { r.status_code = 200 }func (r HttpResponse) updateStatus() string { return fmt.Sprint(r) }func main() {var r1 HttpResponser1.validResponse()fmt.Println(r1.updateStatus())r2 := new(HttpResponse)r2.validResponse()fmt.Println(r2.updateStatus())
}

运行结果如下:

{200}
{200}

五、方法值和方法表达式

代码如下:

package mainimport ("fmt"
)type S struct {Name string
}func (s S) M1() {s.Name = "value"
}
func (s *S) M2() {s.Name = "pointer"
}
func main() {var s1 = S{"new"}var s2 = &s1s1.M2()fmt.Printf("%+v, %+v\n", s1, s2)s1 = S{"new"}s2 = &s1s2.M1()fmt.Printf("%+v, %+v\n", s1, s2)
}

运行结果如下:

{Name:pointer}, &{Name:pointer}
{Name:new}, &{Name:new}

六、方法和未导出字段

七、嵌入式型的方法和继承

1.嵌入类型的方法和继承

代码如下:

package mainimport ("fmt""math"
)type Point struct {x, y float64
}func (p *Point) Abs() float64 {return math.Sqrt(p.x*p.x + p.y*p.y)
}type NamedPoint struct {Pointname string
}func main() {n := &NamedPoint{Point{3, 4}, "Pythongoooo"}fmt.Println(n.Abs())
}

运行结果如下:

5

2.多重继承

代码如下:

package mainimport ("fmt"
)type Camera struct{}func (c *Camera) TakeAPicture() string {return "拍照"
}type Phone struct{}func (p *Phone) Call() string {return "响铃"
}type CameraPhone struct {CameraPhone
}func main() {cp := new(CameraPhone)fmt.Println("我们的新款拍照手机有多种功能:")fmt.Println("打开了相机:", cp.TakeAPicture())fmt.Println("电话来电:", cp.Call())
}

运行结果如下:

我们的新款拍照手机有多种功能:
打开了相机: 拍照
电话来电: 响铃

初始化结构体的成员变量

一、使用“键值对”初始化结构体

键值对初始化的格式如下:

ins:=结构体类型名{

字段1:字段1的值,

字段2:字段2的值,

……

}

二、使用多个值的列表初始化结构体

多个值使用逗号分隔初始化结构体,例如:

ins:=结构体类型号{

字段1的值,

字段2的值,

……

}

代码如下:

package mainimport ("fmt"
)func main() {type Address struct {Province    stringCity        stringZipCode     intPhoneNumber string}addr := Address{"四川","成都",610000,"0",}fmt.Println(addr)
}

运行结果如下:

{四川 成都 610000 0}

三、初始化匿名结构体

代码如下:

package mainimport ("fmt"
)//打印消息类型,传入匿名结构体
func printMsgType(msg *struct {id   intdata string
}) {//使用动词%T打印msg的类型fmt.Printf("%T\n", msg)
}func main() {//实例化一个匿名结构体msg := &struct {     //定义部分id   intdata string   }{                      //值初始化部分1024,"hello",}printMsgType(msg)
}

运行结果如下:

*struct { id int; data string }

构造函数——结构体和类型的一系列初始化操作的函数封装

1、多种方式创建和初始化结构体——模拟构造函数重载

2、带有父子关系的结构体的构造和初始化——模拟父级构造调用

方法:

一、为结构体添加方法

二、接收器——方法作用的目标

接收器的格式如下:

func (接收器变量 接收器类型) 方法名 (参数列表) (返回参数) {

函数体

}

1、理解指针类型的接收器

代码如下:

package mainimport ("fmt"
)//定义属性结构
type Property struct {value int //属性值
}//设置属性值
func (p *Property) SetValue(v int) {//修改p的成员变量p.value = v
}//取属性值
func (p *Property) Value() int {return p.value
}func main() {//实例化属性p := new(Property)//设置值p.SetValue(100)//打印值fmt.Println(p.Value())
}

运行结果如下:

100

2、理解非指针类型的接收器

代码如下:

package mainimport ("fmt"
)//定义点结构
type Point struct {X intY int
}//非指针接收器的加方法
func (p Point) Add(other Point) Point {//成员值与参数相加后返回新的结构return Point{p.X + other.X, p.Y + other.Y}
}func main() {//初始化点p1 := Point{1, 1}p2 := Point{2, 2}//与另外一个点相加result := p1.Add(p2)//输出结果fmt.Println(result)
}

运行结果如下:

{3 3}

三、示例:二维矢量模拟玩家移动

创建一个文件夹playermove,有三个文件

1、实现二维矢量结构

vec.go的代码如下:

package mainimport "math"type Vec2 struct {X, Y float32
}//使用矢量加上另外一个矢量,生成新的矢量
func (v Vec2) Add(other Vec2) Vec2 {return Vec2{v.X + other.X,v.Y + other.Y,}
}//使用矢量减去另外一个矢量,生成新的矢量
func (v Vec2) Sub(other Vec2) Vec2 {return Vec2{v.X - other.X,v.Y - other.Y,}
}//使用矢量乘以另外一个矢量,生成新的矢量
func (v Vec2) Scale(s float32) Vec2 {return Vec2{v.X * s, v.Y * s}
}//计算两个矢量的距离
func (v Vec2) DistanceTo(other Vec2) float32 {dx := v.X - other.Xdy := v.Y - other.Yreturn float32(math.Sqrt(float64(dx*dx + dy*dy)))
}//返回当前矢量的标准化矢量
func (v Vec2) Normalize() Vec2 {mag := v.X*v.X + v.Y*v.Yif mag > 0 {oneOverMag := 1 / float32(math.Sqrt(float64(mag)))return Vec2{v.X * oneOverMag, v.Y * oneOverMag}}return Vec2{0, 0}
}

2、实现玩家对象

player.go的代码如下:

package maintype Player struct {currPos   Vec2targetPos Vec2speed     float32
}//设置玩家移动的目标位置
func (p *Player) MoveTo(v Vec2) {p.targetPos = v
}//获取当前的位置
func (p *Player) Pos() Vec2 {return p.currPos
}//判断是否到达目的地
func (p *Player) IsArrived() bool {//通过计算当前玩家位置与目标位置的距离不超过移动的步长,判断已经到达目标点return p.currPos.DistanceTo(p.targetPos) < p.speed
}//更新玩家的位置
func (p *Player) Update() {if !p.IsArrived() {//计算机出当前位置指向目标的朝向dir := p.targetPos.Sub(p.currPos).Normalize()//添加速度矢量生成新的位置newPos := p.currPos.Add(dir.Scale(p.speed))//移动完成后,更新当前位置p.currPos = newPos}
}//创建新玩家
func NewPlayer(speed float32) *Player {return &Player{speed: speed,}
}

3、处理移动逻辑

main.go的代码如下:

package mainimport "fmt"func main() {//实例化玩家对象,并设速度为0.5p := NewPlayer(0.5)//让玩家移动到3,1点p.MoveTo(vec2{3, 1})//如果没有到达就一直循环for !p.IsArrived() {//更新玩家位置p.Update()//打印每次移动后的玩家位置fmt.Println(p.Pos())}
}

四、为类型添加方法

1、为基本类型添加方法

代码如下:

package mainimport ("fmt"
)//将int定义MyInt类型
type MyInt int//为MyInt添加IsZero()方法
func (m MyInt) IsZero() bool {return m == 0
}//为MyInt添加Add()方法
func (m MyInt) Add(other int) int {return other + int(m)
}func main() {var b MyIntfmt.Println(b.IsZero())b = 1fmt.Println(b.Add(2))
}

运行结果如下:

true
3

2、http包中的类型方法

代码如下:

package mainimport ("fmt""io/ioutil""net/http""os""strings"
)func main() {client := &http.Client{}//创建一个HTTP请求req, err := http.NewRequest("POST", "http://www.163.com", strings.NewReader("key=value"))//发现错误就打印并退出if err != nil {fmt.Println(err)os.Exit(1)return}//为标头添加信息req.Header.Add("User-Agent", "myClient")//开始请求resp, err := client.Do(req)//处理请求的错误if err != nil {fmt.Println(err)os.Exit(1)return}//读取服务器返回的内容data, err := ioutil.ReadAll(resp.Body)fmt.Println(string(data))defer resp.Body.Close()
}

运行结果如下:内容太多了,不复制。

3、time包中的类型方法

代码如下:

package mainimport ("fmt""time"
)func main() {fmt.Println(time.Second.String())
}

运行结果如下:

1s

五、示例:使用事件系统实现事件的响应和处理

1、方法和函数的统一调用

函数代理,detegate.go,代码如下:

package mainimport ("fmt"
)//声明一个结构体
type class struct{}//给结构体添加Do()方法
func (c *class) Do(v int) {fmt.Println("call method do:", v)
}//普通函数的Do()方法
func funcDo(v int) {fmt.Println("call function do:", v)
}func main() {//声明一个函数回调var delegate func(int)//创建结构体实例c := new(class)//将回调设为c的Do方法delegate = c.Do//调用delegate(100)//将回调设为普通函数delegate = funcDo//调用delegate(100)
}

运行结果如下:

call method do: 100
call function do: 100

2、事件系统基本原理

3、事件注册

注册事件,./eventsys/reg.go,代码如下:

//实例化一个通过字符串映射函数切片的map
var eventByName=make(map[string][]func(interface{}))//注册事件,提供事件名和回调函数
func RegisterEvent(name string,callback func(interface{})){//通过名字查找事件列表list:=eventByName[name]//在列表切片中添加函数list=append(list,callback)//保存修改的事件列表切片eventByName[name]=list
}

4、事件调用

调用事件,./eventsys/reg.go,代码如下:

//调用事件
func CallEvent(name string, param interface{}) {//通过名字找到事件列表list := eventByName[name]//遍历这个事件的所有回调for _, callback := range list {//传入参数调用回调callback(param)}
}

5、使用事件系统

使用事件系统,./eventsys/main.go,代码如下:

package mainimport "fmt"//声明角色的结构体
type Actor struct{}//为角色添加一个事件处理函数
func (a *Actor) OnEvent(param interface{}) {fmt.Println("actor event:", param)
}//全局事件
func GlobalEvent(param interface{}) {fmt.Println("global event:", param)
}func main() {//实例化一个角色a := new(Actor)//注册名为OnSkill的回调RegisterEvent("OnSkill", a.OnEvent)//再次在OnSkill上注册全局事件RegisterEvent("OnSkill", GlobalEvent)//调用事件,所有注册的同名函数都会被调用CallEvent("OnSkill", 100)
}

书上的运行结果 如下:

actor event:100

global event:100

类型内嵌和结构体内嵌

一、声明结构体内嵌

代码如下:

package mainimport ("fmt"
)//基础颜色
type BasicColor struct {//红、绿、蓝三种颜色分量R, G, B float32
}//完整颜色定义
type Color struct {//将基本颜色作为成员Basic BasicColor//透明度Alpha float32
}func main() {var c Color//设置基本颜色分量c.Basic.R = 1c.Basic.G = 1c.Basic.B = 0//设置透明度c.Alpha = 1//显示整个结构体内容fmt.Printf("%+v", c)
}

运行结果如下:

{Basic:{R:1 G:1 B:0} Alpha:1}

代码如下:

package mainimport ("fmt"
)//基础颜色
type BasicColor struct {//红、绿、蓝三种颜色分量R, G, B float32
}//完整颜色定义
type Color struct {//将基本颜色作为成员BasicColor//透明度Alpha float32
}func main() {var c Color//设置基本颜色分量c.R = 1c.G = 1c.B = 0//设置透明度c.Alpha = 1//显示整个结构体内容fmt.Printf("%+v", c)
}

运行结果如下:

{BasicColor:{R:1 G:1 B:0} Alpha:1}

二、结构内嵌特性

三、使用组合思想描述对象特性

人和鸟的特性,代码如下:

package mainimport ("fmt"
)//可飞行的
type Flying struct{}func (f *Flying) Fly() {fmt.Println("can fly")
}//可行走的
type Walkable struct{}func (f *Walkable) Walk() {fmt.Println("can walk")
}//人类
type Human struct {Walkable //人类能行走
}//鸟类
type Bird struct {Walkable //鸟类能行走Flying   //鸟类能飞行
}func main() {//实例化鸟类b := new(Bird)fmt.Println("Bird:")b.Fly()b.Walk()//实例化人类h := new(Human)fmt.Println("Human:")h.Walk()
}

运行结果如下:

Bird:
can fly
can walk
Human:
can walk

四、初始化结构体内嵌

车辆结构的组装和初始化,代码如下:

package mainimport ("fmt"
)//车轮
type Wheel struct {Size int
}//引擎
type Engine struct {Power int    //功率Type  string //类型
}//车
type Car struct {WheelEngine
}func main() {c := Car{//初始化轮子Wheel: Wheel{Size: 18,},//初始化引擎Engine: Engine{Type:  "1.4T",Power: 143,},}fmt.Printf("%+v\n", c)
}

运行结果如下:

{Wheel:{Size:18} Engine:{Power:143 Type:1.4T}}

五、初始化内嵌匿名结构体

内嵌结构体,代码如下:

package mainimport ("fmt"
)//车轮
type Wheel struct {Size int
}//车
type Car struct {Wheel//引擎Engine struct {Power int    //功率Type  string //类型}
}func main() {c := Car{//初始化轮子Wheel: Wheel{Size: 18,},//初始化引擎Engine: struct {Power intType  string}{Type:  "1.4T",Power: 143,},}fmt.Printf("%+v\n", c)
}

运行结果如下:

{Wheel:{Size:18} Engine:{Power:143 Type:1.4T}}

六、成员名字冲突

代码如下:

package mainimport ("fmt"
)type A struct {a int
}type B struct {a int
}type C struct {AB
}func main() {c := &C{}c.A.a = 1fmt.Println(c)
}

运行结果如下:

&{{1} {0}}

示例:使用匿名结构体分离JSON数据

代码如下:

package mainimport ("encoding/json""fmt"
)//定义手机屏幕
type Screen struct {Size       float32 //屏幕尺寸ResX, ResY int     //屏幕水平和垂直分辨率
}//定义电池
type Battery struct {Capacity int //容量
}//生成JSON数据
func getJsonData() []byte {//完整数据结构raw := &struct {ScreenBatteryHasTouchID bool}{//屏幕参数Screen: Screen{Size: 5.5,ResX: 1920,ResY: 1080,},//电池参数Battery: Battery{2910,},//是否有指纹识别HasTouchID: true,}//将数据序列化为JSONjsonData, _ := json.Marshal(raw)return jsonData
}func main() {//生成一段JSON数据jsonData := getJsonData()fmt.Println(string(jsonData))//只需要屏幕和指纹识别信息的结构和实例screenAndTouch := struct {ScreenHadTouchID bool}{}//反序列化到screenAndTouch中json.Unmarshal(jsonData, &screenAndTouch)//输出screenAndTouch的详细结构fmt.Printf("%+v\n", screenAndTouch)//只需要电池和指纹识别信息的结构和实例batteryAndTouch := struct {BatteryHasTouchID bool}{}//反序列化到batteryAndTouchjson.Unmarshal(jsonData, &batteryAndTouch)//输出screenAndTouch的详细结构fmt.Printf("%+V\n", batteryAndTouch)
}

运行结果如下:

{"Size":5.5,"ResX":1920,"ResY":1080,"Capacity":2910,"HasTouchID":true}
{Screen:{Size:5.5 ResX:1920 ResY:1080} HadTouchID:false}
{{%!V(int=+2910)} %!V(bool=true)}

Go语言的使用结构体、指针和方法相关推荐

  1. c语言如何传递结构体指针,注意使用结构体指针给函数传递参数。

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 为了增加参数调用的灵活性,可以利用结构体指针传递多个不同的参数进入C语言函数体. 最著名的例子就是用Win32API HANDLE CreateThrea ...

  2. c语言中定义结构体指针的作用,C语言结构体定义,使用及指针(1)

    前言 C语言中的结构体作为一大难点,并且对于需要学习数据结构的同学来说,这个结构是每次上课都会遇到的,所以,我们只有更深层次的理解结构体的知识,才能更好的掌握数据结构 结构体用处 结构体到底有什么用? ...

  3. c语言结构体指针使用方法,C语言结构体指针的使用方法

    1.首先定义一个结构体,给它取别名: typedef struct node{ struct node * next://指向下一节点 int data://数据域 }pnode,*linklist; ...

  4. C语言学习笔记---结构体指针

      既然结构体是一种数据类型,那么除了使用变量本身来访问结构体之外,也可以使用指针来访问结构体.下面通过一个简单的例子来演示一下: struct Date{int year;int month;int ...

  5. c语言中定义结构体指针并指向一片内存空间和直接定义一个结构体变量的区别 Node *p=(Node *)malloc(sizeof(Node)); 和 Node p 两个有什么区别??? Node是一

    (来自百度解答) Node *p是定义一个Node结构体指针,指针名为p,指向一个Node结构体,并为这个结构体分配了内存空间 该结构体无命名 Node p是定义一个Node结构体,结构体名为p. 追 ...

  6. Go-结构体类型详解(声明、初始化、结构体指针、方法、序列化等)

    目录 结构体类型(struct) 声明 声明并初始化 结构体指针 方法 Generate 方法和函数的区别 匿名结构体 序列化 全部代码 截图 结构体类型(struct) 去除了C++等语言的的继承. ...

  7. c语言中定义结构体指针的作用,c语言定义结构体指针

    int main() { typedef struct { u8 KeyCurrentIndex;//当前状态索引号 u8 KeyEnterState;//按下[enter]键时转向的索引号 u8 K ...

  8. C语言中的结构体指针赋值问题

    在对数组或者接收到的数据进行赋值时,往往因为结构体中含有指针而无法获取接收到的数据,那么如何才能获取结构体中的指针指向的内容呢?这就涉及到两个部分的内容. 1.通过memcpy()赋值或者对数据进行结 ...

  9. C语言结构体和结构体指针的简单用法

    C语言结构体和结构体指针的简单用法 这里总结一下自己的学习笔记,关于C语言当中的结构体指针的用法,以及结构体简单使用. 简单介绍一下今天出场的嘉宾–>结构体和结构体指针 什么是结构体: 1,定义 ...

  10. C语言结构体与结构体指针的使用

    C语言结构体与结构体指针的使用 tips:最近要好好学习一下数据结构,所以必不可少的就是c语言的结构体和结构体指针了,所以就整理一下这些基础知识 c语言结构体(struct)是由一系列具有相同类型或不 ...

最新文章

  1. 腾讯发布95页重磅报告:全面预测中国互联网未来5年趋势
  2. SMB(Server Message Block) Protocal Research
  3. 坚持,这两个字非常重要!
  4. 数据结构——字符串(未完)
  5. 使用c:foreach时链接失效
  6. JS正则表达式验证数字非常全 - 吾心无所 - 博客园
  7. WORD关于论文格式设置
  8. 【Jmeter篇】jmeter+Ant+Jenkins实现自动化测试集成(一)
  9. 2016科学数据大会临时通知
  10. MVC系列博客之排球计分(六)Controller的实现(二)
  11. 智慧酒店:锐捷网络打造的完美酒店
  12. Linux下卸载anaconda3
  13. 三种非对称加密算法总结
  14. android型号的平板电脑,AUTOID Pad /Air-(win)/Air-(android)工业级平板电脑
  15. 一枚芯片的赶考之路:究竟该如何看待“AI评测”?
  16. 抖音xlog算法解析
  17. 13.6.3 程序案例:BLE低功耗蓝牙调试助手
  18. 万物互联时代的操作系统报告 附下载地址
  19. flowable 监听器
  20. QtAndroid详解(1):QAndroidJniObject

热门文章

  1. 为什么要与MCN签约
  2. 有线耳机和无线耳机的利弊
  3. B站收藏 6.1w !GitHub 标星 3.9k !这门神课拯救了我薄弱的计算机基础
  4. 零基础学画画,找对老师重要吗?
  5. c语言分隔符的作用,句子分割代码(C语言程序的语句分隔符是)
  6. 一个简单的个人介绍html网页制作
  7. 英文版Ubuntu16.04安装fcitx-googlepinyin
  8. 华东师大计算机系博士几年,2018华东师范大学就业结果公布!就业率超97%,博士生月薪1.1万!...
  9. Hive2.1.0集成Tez 1
  10. 金刚铠甲心咒全文注音