go语言的结构体、指针、方法详解
资源来自:https://blog.csdn.net/DXB2021/article/details/122652779
结体体定义如下:
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 main
type Player struct {
Name string
HealthPoint int
MagicPoint int
}
func main() {
tank := new(Player)
tank.Name = "Canon"
tank.HealthPoint = 300
}
取结构体的地址实例化:
取地址格式如下:
ins := &T{}
T表示结构体类型。
ins为结构体的实例,类型为*T,是指针类型。
声明并创建一个简单的结构体,程序清单如下:
package main
import(
"fmt"
)
type Movie struct{ //结构体
Name string
Rating float32
}
func main(){
m:=Movie{
Name:"Citizen Kane",
Rating:10,
}
fmt.Println(m.Name,m.Rating)
}
运行结果如下:
Citizen Kane 10
声明一个类型为结构体的变量,程序清单如下:
package main
import(
"fmt"
)
type Movie struct{ //结构体
Name string
Rating float32
}
func main(){
var m Movie
fmt.Printf("%+v\n",m) //零值
m.Name="Metropolis"
m.Rating=0.9918
fmt.Printf("%+v\n",m)
}
运行结果如下:
{Name: Rating:0}
{Name:Metropolis Rating:0.9918}
使用关键字new创建结构体实例,程序清单如下:
package main
import(
"fmt"
)
type Movie struct{ //结构体
Name string
Rating float32
}
func main(){
m:=new(Movie)
m.Name="Metropolis"
m.Rating=0.99
fmt.Printf("%+v\n",m)
}
运行结果如下:
&{Name:Metropolis Rating:0.99}
代码如下:
package main
import (
"fmt"
)
type myStruct struct {
i1 int
f1 float32
str string
}
func main() {
ms := new(myStruct)
ms.i1 = 10
ms.f1 = 15.5
ms.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 main
import(
"fmt"
)
type Movie struct{ //结构体
Name string
Rating float32
}
func main(){
m:=Movie{
Name:"Metropolis",
Rating:0.99,
}
fmt.Printf("%+v\n",m)
}
运行结果如下:
{Name:Metropolis Rating:0.99}
使用简短变量赋值创建嵌套结构体实例,程序清单如下:
package main
import(
"fmt"
)
type Superhero struct{
Name string
Age int
Address Address
}
type Address struct{
Number int
Street string
City 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 main
import(
"fmt"
)
type Alarm struct{
Time string
Sound 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 main
import(
"fmt"
)
type Drink struct{
Name string
Ice 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 main
import(
"fmt"
"reflect"
)
type Drink struct{
Name string
Ice 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 main
import(
"fmt"
)
type Drink struct{
Name string
Ice bool
}
func main(){
a:=Drink{
Name:"Lemonade",
Ice:true,
}
b:=a
b.Ice=false
fmt.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 main
import(
"fmt"
)
type Drink struct{
Name string
Ice bool
}
func main(){
a:=Drink{
Name:"Lemonade",
Ice:true,
}
b:=&a
b.Ice=false
fmt.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 main
import (
"fmt"
"strings"
)
type Person struct {
firstName string
lastName string
}
func upPerson(p *Person) {
p.firstName = strings.ToUpper(p.firstName)
p.lastName = strings.ToUpper(p.lastName)
}
func main() {
//作为值类型
var pers1 Person
pers1.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_pack
type ExpStruct struct {
Mi1 int
Mf1 float32
}
下面是main.go的代码:
package main
import (
"fmt"
"./struct_pack/struckPack"
)
func main() {
struct1:=new(structPack.Expstruct)
struct1.Mi1=10
struct1.Mf1=16.
fmt.Prinf("Mi1 = %d\n",struct.Mi1)
fmt.Prinf("Mf1 = %d\n",struct.Mf1)
}
编译执行输出:(编译失败)
Mi1=10
Mf1=16.000000
2、结构体成员访问
在Go语言中,访问结构体成员需要使用点号操作符,格式为“结构体.成员名”。
代码如下:
package main
import (
"fmt"
)
type Employee struct {
ID int
Name string
Address string
Phone string
}
func main() {
var employee1 Employee
employee1.ID = 10001
employee1.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 main
import (
"fmt"
)
type Employee struct {
ID int
Name string
Address string
Phone 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 Employee
employee.ID = 10001
employee.Name = "Tom"
employee.Address = "xxxx"
employee.Phone = "1881412xxxx"
printEmployee(&employee)
}
运行结果如下:
Employee ID: 10001
Employee Name: Tom
Employee Address: xxxx
Employee Phone: 1881412xxxx
3、结构体参数传输
代码如下:
package main
import (
"fmt"
)
type Employee struct {
ID int
Name string
Address string
Phone string
}
//形式传参
func operateEmployee1(employee Employee) {
employee.ID = 10010
}
//指针传参
func operateEmployee2(employee *Employee) {
employee.ID = 10010
}
func main() {
var employee Employee
employee.ID = 10001
employee.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 main
import (
"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 main
import (
"fmt"
)
type firstS struct {
in1 int
in2 int
}
type secondS struct {
b int
c float32
int //匿名字段
firstS //匿名字段
}
func main() {
sec := new(secondS)
sec.b = 6
sec.c = 7.5
sec.int = 60
sec.in1 = 5
sec.in2 = 10
fmt.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 main
import (
"fmt"
)
type A struct {
ax, ay int
}
type B struct {
A
bx, 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 main
import (
"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 main
import (
"fmt"
)
type Writer interface {
Write()
}
type Author struct {
name string
Writer
}
// 定义新结构体,重点是实现接口方法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 main
import (
"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 = 102
stu.Human.name = "Titan"
stu.Gender = "男"
stu.Human.Age = 14
stu.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) // 初始化时已自动给予零值:0
fmt.Printf("Student.Human.name is: %s\n", stu.name) // (*stu).name
fmt.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) // 初始化时已自动给予零值:0
fmt.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 main
import (
"fmt"
)
type TwoInts struct {
a int
b 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 = 12
two1.b = 10
fmt.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 main
import (
"fmt"
)
type IntVector []int
func (v IntVector) Sum() (s int) {
for _, x := range v {
s += x
}
return
}
func main() {
fmt.Println(IntVector{1, 2, 3}.Sum())
}
运行结果如下:
6
代码如下:
package main
import (
"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 main
import (
"fmt"
)
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
func main() {
var a Integer = 1
if a.Less(2) {
fmt.Println(a, "Less 2")
}
}
运行结果如下:
1 Less 2
代码如下:
package main
import (
"fmt"
)
type Integer int
func Integer_Less(a Integer, b Integer) bool {
return a < b
}
func main() {
var a Integer = 1
if Integer_Less(a, 2) {
fmt.Println(a, "Less 2")
}
}
运行结果如下:
1 Less 2
三、工厂方法创建结构体
四、基于指针对象的方法
代码如下:
package main
import (
"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 HttpResponse
r1.validResponse()
fmt.Println(r1.updateStatus())
r2 := new(HttpResponse)
r2.validResponse()
fmt.Println(r2.updateStatus())
}
运行结果如下:
{200}
{200}
五、方法值和方法表达式
代码如下:
package main
import (
"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 = &s1
s1.M2()
fmt.Printf("%+v, %+v\n", s1, s2)
s1 = S{"new"}
s2 = &s1
s2.M1()
fmt.Printf("%+v, %+v\n", s1, s2)
}
运行结果如下:
{Name:pointer}, &{Name:pointer}
{Name:new}, &{Name:new}
六、方法和未导出字段
七、嵌入式型的方法和继承
1.嵌入类型的方法和继承
代码如下:
package main
import (
"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 {
Point
name string
}
func main() {
n := &NamedPoint{Point{3, 4}, "Pythongoooo"}
fmt.Println(n.Abs())
}
运行结果如下:
5
2.多重继承
代码如下:
package main
import (
"fmt"
)
type Camera struct{}
func (c *Camera) TakeAPicture() string {
return "拍照"
}
type Phone struct{}
func (p *Phone) Call() string {
return "响铃"
}
type CameraPhone struct {
Camera
Phone
}
func main() {
cp := new(CameraPhone)
fmt.Println("我们的新款拍照手机有多种功能:")
fmt.Println("打开了相机:", cp.TakeAPicture())
fmt.Println("电话来电:", cp.Call())
}
运行结果如下:
我们的新款拍照手机有多种功能:
打开了相机: 拍照
电话来电: 响铃
初始化结构体的成员变量
一、使用“键值对”初始化结构体
键值对初始化的格式如下:
ins:=结构体类型名{
字段1:字段1的值,
字段2:字段2的值,
……
}
二、使用多个值的列表初始化结构体
多个值使用逗号分隔初始化结构体,例如:
ins:=结构体类型号{
字段1的值,
字段2的值,
……
}
代码如下:
package main
import (
"fmt"
)
func main() {
type Address struct {
Province string
City string
ZipCode int
PhoneNumber string
}
addr := Address{
"四川",
"成都",
610000,
"0",
}
fmt.Println(addr)
}
运行结果如下:
{四川 成都 610000 0}
三、初始化匿名结构体
代码如下:
package main
import (
"fmt"
)
//打印消息类型,传入匿名结构体
func printMsgType(msg *struct {
id int
data string
}) {
//使用动词%T打印msg的类型
fmt.Printf("%T\n", msg)
}
func main() {
//实例化一个匿名结构体
msg := &struct { //定义部分
id int
data string
}{ //值初始化部分
1024,
"hello",
}
printMsgType(msg)
}
运行结果如下:
*struct { id int; data string }
构造函数——结构体和类型的一系列初始化操作的函数封装
1、多种方式创建和初始化结构体——模拟构造函数重载
2、带有父子关系的结构体的构造和初始化——模拟父级构造调用
方法:
一、为结构体添加方法
二、接收器——方法作用的目标
接收器的格式如下:
func (接收器变量 接收器类型) 方法名 (参数列表) (返回参数) {
函数体
}
1、理解指针类型的接收器
代码如下:
package main
import (
"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 main
import (
"fmt"
)
//定义点结构
type Point struct {
X int
Y 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 main
import "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.X
dy := v.Y - other.Y
return float32(math.Sqrt(float64(dx*dx + dy*dy)))
}
//返回当前矢量的标准化矢量
func (v Vec2) Normalize() Vec2 {
mag := v.X*v.X + v.Y*v.Y
if 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 main
type Player struct {
currPos Vec2
targetPos Vec2
speed 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 main
import "fmt"
func main() {
//实例化玩家对象,并设速度为0.5
p := NewPlayer(0.5)
//让玩家移动到3,1点
p.MoveTo(vec2{3, 1})
//如果没有到达就一直循环
for !p.IsArrived() {
//更新玩家位置
p.Update()
//打印每次移动后的玩家位置
fmt.Println(p.Pos())
}
}
四、为类型添加方法
1、为基本类型添加方法
代码如下:
package main
import (
"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 MyInt
fmt.Println(b.IsZero())
b = 1
fmt.Println(b.Add(2))
}
运行结果如下:
true
3
2、http包中的类型方法
代码如下:
package main
import (
"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 main
import (
"fmt"
"time"
)
func main() {
fmt.Println(time.Second.String())
}
运行结果如下:
1s
五、示例:使用事件系统实现事件的响应和处理
1、方法和函数的统一调用
函数代理,detegate.go,代码如下:
package main
import (
"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 main
import "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 main
import (
"fmt"
)
//基础颜色
type BasicColor struct {
//红、绿、蓝三种颜色分量
R, G, B float32
}
//完整颜色定义
type Color struct {
//将基本颜色作为成员
Basic BasicColor
//透明度
Alpha float32
}
func main() {
var c Color
//设置基本颜色分量
c.Basic.R = 1
c.Basic.G = 1
c.Basic.B = 0
//设置透明度
c.Alpha = 1
//显示整个结构体内容
fmt.Printf("%+v", c)
}
运行结果如下:
{Basic:{R:1 G:1 B:0} Alpha:1}
代码如下:
package main
import (
"fmt"
)
//基础颜色
type BasicColor struct {
//红、绿、蓝三种颜色分量
R, G, B float32
}
//完整颜色定义
type Color struct {
//将基本颜色作为成员
BasicColor
//透明度
Alpha float32
}
func main() {
var c Color
//设置基本颜色分量
c.R = 1
c.G = 1
c.B = 0
//设置透明度
c.Alpha = 1
//显示整个结构体内容
fmt.Printf("%+v", c)
}
运行结果如下:
{BasicColor:{R:1 G:1 B:0} Alpha:1}
二、结构内嵌特性
三、使用组合思想描述对象特性
人和鸟的特性,代码如下:
package main
import (
"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 main
import (
"fmt"
)
//车轮
type Wheel struct {
Size int
}
//引擎
type Engine struct {
Power int //功率
Type string //类型
}
//车
type Car struct {
Wheel
Engine
}
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 main
import (
"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 int
Type string
}{
Type: "1.4T",
Power: 143,
},
}
fmt.Printf("%+v\n", c)
}
运行结果如下:
{Wheel:{Size:18} Engine:{Power:143 Type:1.4T}}
六、成员名字冲突
代码如下:
package main
import (
"fmt"
)
type A struct {
a int
}
type B struct {
a int
}
type C struct {
A
B
}
func main() {
c := &C{}
c.A.a = 1
fmt.Println(c)
}
运行结果如下:
&{{1} {0}}
示例:使用匿名结构体分离JSON数据
代码如下:
package main
import (
"encoding/json"
"fmt"
)
//定义手机屏幕
type Screen struct {
Size float32 //屏幕尺寸
ResX, ResY int //屏幕水平和垂直分辨率
}
//定义电池
type Battery struct {
Capacity int //容量
}
//生成JSON数据
func getJsonData() []byte {
//完整数据结构
raw := &struct {
Screen
Battery
HasTouchID bool
}{
//屏幕参数
Screen: Screen{
Size: 5.5,
ResX: 1920,
ResY: 1080,
},
//电池参数
Battery: Battery{
2910,
},
//是否有指纹识别
HasTouchID: true,
}
//将数据序列化为JSON
jsonData, _ := json.Marshal(raw)
return jsonData
}
func main() {
//生成一段JSON数据
jsonData := getJsonData()
fmt.Println(string(jsonData))
//只需要屏幕和指纹识别信息的结构和实例
screenAndTouch := struct {
Screen
HadTouchID bool
}{}
//反序列化到screenAndTouch中
json.Unmarshal(jsonData, &screenAndTouch)
//输出screenAndTouch的详细结构
fmt.Printf("%+v\n", screenAndTouch)
//只需要电池和指纹识别信息的结构和实例
batteryAndTouch := struct {
Battery
HasTouchID bool
}{}
//反序列化到batteryAndTouch
json.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)}
————————————————
版权声明:本文为CSDN博主「DXB2021」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/DXB2021/article/details/122652779
posted on 2022-08-23 10:18 Ray(Mr.huang) 阅读(335) 评论(0) 编辑 收藏 举报