6.1定义结构体
定义格式:
type 类型名 struct{
字段1 字段1类型
字段2 字段2类型
...
}
6.2实例化结构体--为结构体分配内存并初始化
1、基本的实例化形式
var ins T
T为结构体类型,ins为结构体的实例。
2、创建指针类型的结构体
ins:=new(T)
3、取结构体的地址实例化(应用最广泛)
ins:=&T{}
6.3初始化结构体的成员变量
初始化有两种形式:一种是字段“键值对”形式,一种是多个值的列表形式。
1、使用“键值对”初始化结构体
格式
ins:=结构体类型名{
字段1:字段1的值,
字段2:字段2的值,
...
}
例子:
type People struct{
name string
chile *People
}
relation:=&People{
name:"爷爷",
child:&People{
name:"爸爸",
child:&People{
name:"我",
},
},
}
2、使用多个值的列表初始化结构体
格式
ins:=结构体类型名{
字段1的值,
字段2的值,
...
}
注意:
这种方式必须初始化结构体的所有字段,每一个初始值的填充顺序必须与字段在结构体中的声明顺序一致,键值对与值列表的初始化形式不能混用。
例子:
type Address struct{
Province string
City string
Zip_Code int
Phone_Number string
}
addr:=Address{
"四川",
"成都",
610000,
"0",
}
fmt.Println(addr)
3、初始化匿名结构体
(1)匿名结构体定义格式和初始化写法
ins:=struct{
//匿名结构体字段定义
字段1 字段类型1
字段2 字段类型2
...
}{
//字段值初始化
初始化字段1:字段1的值,
初始化字段2:字段2的值,
...
}
//键值对初始化部分是可选的,不初始化成员时,匿名结构体格式变为
ins:=struct{
//匿名结构体字段定义
字段1 字段类型1
字段2 字段类型2
...
}{}
(2)使用匿名结构体的例子
//打印消息类型,传入匿名结构体
func print_Msg_Type(msg *struct{
id int
data string
}) {
//使用动词%T打印msg的类型
fmt.Printf("%T\n",msg)
}
func main() {
//实例化一个匿名结构体
msg:=&struct {
//定义部分
id int
data string
}{
//值初始化部分
1024,
"hello",
}
print_Msg_Type(msg)
}
6.4构造函数--结构体和类型的一系列初始化操作的函数封装
Go语言的类型或结构体没有构造函数的功能。结构体的初始化过程可以使用函数封装实现。
1、多种方式创建和初始化结构体--模拟构造函数重载
type Cat struct{
Color sting
Name string
}
func New_Cat_By_Name(name string) *Cat{
return &Cat{
Name:name,
}
}
func New_Cat_By_Color(color string) *Cat{
return &Cat{
Color:color,
}
}
Go语言中没有函数重载
2、带有父子关系的结构体的构造和初始化--模拟父级构造调用
type Cat struct{
Color string
Name string
}
type Black_Cat struct{
Cat//嵌入Cat类似于派生
}
//“构造基类”
func New_Cat(name string) *Cat{
return &Cat{
Name:name,
}
}
//构造子类
func New_Black_Cat(color string)*Black_Cat{
cat:=&Black_Cat{}//实例化Black_Cat结构,此时Cat也同时被实例化
cat.Color=color
return cat
}
6.5方法
Go语言中的方法是一种作用于特定类型变量的函数。这种特定类型变量叫做接收器。
1、为结构体添加方法
(1)面向过程实现方法
package main
type Bag struct {
items []int
}
//将物品放入背包的过程
func Insert(b *Bag,itemid int) {
b.items=append(b.items,itemid)
}
func main() {
//bag:=new(Bag)
bag:=&Bag{}
Insert(bag,1001)
}
(2)Go语言的结构体方法
package main
type Bag struct {
items []int
}
//将物品放入背包,(b *Bag)表示接收器即Insert作用的对象的实例,
// 相当于面向对象语言的this,self,每个方法只能有一个接收器
func (b *Bag)Insert(itemid int) {
b.items=append(b.items,itemid)
}
func main() {
b:=new(Bag)
b.Insert(1001)
}
2、接收器--方法作用的目标
接收器格式如下:
func(接收器变量 接收器类型)方法名(参数列表)(返回参数){
函数体
}
接收器分为指针类型接收器和非指针类型接收器
(1)理解指针类型的接收器
package main
import "fmt"
//定义属性结构
type Property struct {
value int//属性值
}
//设置属性值
func (p *Property)set_Value(v int) {
//修改p的成员变量
p.value=v
}
//取属性值
func (p *Property)get_Value() int {
return p.value
}
func main() {
//实例化属性
p:=new(Property)
//设置值
p.set_Value(100)
//打印值
fmt.Println(p.get_Value())
}
(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、示例:二维矢量模拟玩家移动
(1)实现二维矢量结构
//实现二维矢量结构
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)Distance_To(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{
one_Over_Mag:=1/float32(math.Sqrt(float64(mag)))
return Vec2{v.x*one_Over_Mag,v.Y*one_Over_Mag}
}
return Vec2{0,0}
}
(2)实现玩家对象
package main
type Player struct {
curr_Pos Vec2//当前位置
target_Pos Vec2//目标位置
speed float32//移动速度
}
//设置玩家移动的目标位置
func (p *Player)Move_To(v Vec2) {
p.target_Pos=v
}
//获取当前位置
func (p *Player)Pos()Vec2 {
return p.curr_Pos
}
//判断是否到达目的地
func (p *Player)Is_Arrived()bool {
//通过计算当前玩家位置与目标位置的距离不超过移动的步长,判断已经到达目标点
return p.curr_Pos.Distance_To(p.target_Pos)<p.speed
}
//更新玩家位置
func (p *Player)Update() {
if !p.Is_Arrived(){
//计算出当前位置指向目标的朝向
dir:=p.target_Pos.Sub(p.curr_Pos).Normalize()
//添加速度矢量生成新的位置
new_Pos:=p.curr_Pos.Add(dir.Scale(p.speed))
//移动完成后,更新当前位置
p.curr_Pos=new_Pos
}
}
//创建新玩家
func New_Player(speed float32)*Player {
return &Player{speed:speed,}
}
(3)处理移动逻辑
//实现二维矢量结构
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)Distance_To(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{
one_Over_Mag:=1/float32(math.Sqrt(float64(mag)))
return Vec2{v.X*one_Over_Mag,v.Y*one_Over_Mag}
}
return Vec2{0,0}
}
这里遇到了跨文件调用的问题。解决方法:https://blog.csdn.net/qq_36214481/article/details/88791692
4、为类型添加方法
(1)为基本类型添加方法
package main
import "fmt"
//将int定义为My_Int
type My_Int int
//为My_Int添加Is_Zero()方法
func (m My_Int)Is_Zero()bool {
return m==0
}
//为My_Int添加Add()方法
func (m My_Int)Add(other int) int {
return int(m)+other
}
func main() {
var b My_Int
fmt.Println(b.Is_Zero())
b=1
fmt.Println(b.Add(2))
}
(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.sougo.com/",strings.NewReader("key=value"))
//发现错误就打印退出
if err!=nil{
fmt.Println(err)
os.Exit(1)
return
}
//为标头添加信息
req.Header.Add("User-Agent","my Client")
//开始请求
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()
}
5、示例:使用事件系统实现事件的响应和处理
(1)方法和函数的统一调用
结构体方法和普通函数的参数完全一致,也就是方法和函数的签名一致。
package main
import "fmt"
//声明一个结构体
type class struct {
}
//给结构体添加Do()方法
func (c *class)Do(v int) {
fmt.Println("call method do:",v)
}
//普通函数的Do()方法
func func_Do(v int) {
fmt.Println("call method do:",v)
}
func main() {
//声明一个函数回调
var delegate func(int)
//创建结构体实例
c:=new(class)
//将回调设为c的Do方法
delegate=c.Do
//调用
delegate(100)
//将回调函数设为普通函数
delegate=func_Do
//调用
delegate(100)
}
(2)事件系统基本原理
事件系统的特点
i 能够实现事件的一方,可以根据事件ID或名字注册对应的事件。
ii 事件发起者,会根据注册信息通知这些注册者。
iii 一个事件可以有多个实现方响应
(3)注册事件
事件注册的过程就是将事件名称和响应函数关联并保存起来。
package main
//实例化一个通过字符串映射函数切片的map
var event_By_Name=make(map[string][]func(interface{}))
//注册事件,提供事件名和回调函数
func Register_Event(name string,callback func(interface{})) {
//通过名字查找事件列表
list:=event_By_Name[name]
//在列表切片中添加函数
list=append(list,callback)
//保存修改的事件列表切片
event_By_Name[name]=list
}
(4)事件调用
//调用事件
func call_Event(name string,param interface{}) {
//通过名字查找事件列表
list:=event_By_Name[name]
//遍历这个事件的所有回调
for _,callback:=range list{
//传入参数调用回调
callback(param)
}
}
(5)使用事件系统
package main
import "fmt"
type Actor struct {
}
//为角色添加一个事件处理函数
func (a *Actor)On_Event(param interface{}) {
fmt.Println("actor event:",param)
}
//全局事件
func Global_Event(param interface{}) {
fmt.Println("global event:",param)
}
func main() {
//实例化一个角色
a:=new(Actor)
//注册名为On_Skill的回调
Register_Event("On_Skill",a.On_Event)
//再次在On_Skill上注册全局事件
Register_Event("On_Skill",Global_Event)
//调用事件,所有注册的同名函数都会被调用
call_Event("On_Skill",100)
}
6.6类型内嵌和结构体内嵌
结构体允许其成员字段在声明时没有字段名而只有类型,这种形式的字段被称为类型内嵌或匿名字段。
type Data struct{
int
float32
bool
}
ins:=&Data{
int: 10,
float32: 3.14,
bool: true,
}
类型内嵌其实仍然拥有自己的字段名,只是字段名就是其类名本身,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。
结构体实例化后,如果匿名的字段类型为结构体,那么可以直接访问匿名结构体里的所有成员,这种方法被称为结构体内嵌。
1、声明结构体内嵌
非内嵌写法的例子
package main
import "fmt"
//基础颜色
type Basic_Color struct {
R,G,B float32
}
//完整颜色
type Color struct {
Basic Basic_Color
//透明度
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)
}
内嵌写法
package main
import "fmt"
type Basic_Color struct {
R,G,B float32
}
type Color struct {
Basic_Color
Alpha float32
}
func main() {
var c Color
c.R=1
c.G=1
c.B=0
c.Alpha=1
fmt.Printf("%+v",c)
}
2、结构体内嵌特性
(1)内嵌的结构体可以直接访问其成员变量
(2)内嵌结构体的字段名是它的类型名
3、使用组合思想描述对象特性
Go语言中的结构体内嵌特性就是一种组合特性,使用组合特性可以快速构建对象的不同特性。
例子:人和鸟的特征
package main
import "fmt"
//可飞行的
type Flying struct {
}
func (f *Flying)Fly() {
fmt.Println("can fly")
}
//可行走的
type Walkable struct {
}
func (w *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()
}
4、初始化结构体内嵌
结构体内嵌初始化时,将结构体内嵌的类型作为字段名像普通结构体一样进行初始化。
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)
}
5、初始化内嵌匿名结构体
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)
}
6.7示例:使用匿名结构体分离JSON数据
package main
import (
"encoding/json"
"fmt"
)
//定义手机屏幕
type Screen struct {
Size float32//屏幕大小
Res_X,Res_Y int//屏幕水平和垂直分辨率
}
//定义电池
type Battery struct {
Capacity int//电池容量
}
//生成JSON数据
func gen_Json_Data()[]byte {
//完整数据结构
raw:=&struct {
Screen
Battery
Has_Touch_ID bool//序列化时添加的字段:是否有指纹识别
}{
//屏幕参数
Screen{
Size:5.5,
Res_X:1920,
Res_Y:1080,
},
//电池参数
Battery{
2910,
},
//是否有指纹识别
true,
}
//将数据序列化为JSON
json_Data,_:=json.Marshal(raw)
return json_Data
}
func main() {
//生成一段JSON数据
json_Data:=gen_Json_Data()
fmt.Println(string(json_Data))
//只需要屏幕和指纹识别信息的结构和实例
screen_And_Touch:= struct {
Screen
Has_Touch_ID bool
}{}
//反序列化到screen_And_Touch
json.Unmarshal(json_Data,&screen_And_Touch)
//输出screen_And_Touch的详细结构
fmt.Printf("%+v\n",screen_And_Touch)
//只需要电池和指纹识别信息的结构和实例
battery_And_Touch:= struct {
Battery
Has_Touch_ID bool
}{}
//反序列化到battery_And_Touch
json.Unmarshal(json_Data,battery_And_Touch)
//输出battery_And_Touch的详细结构
fmt.Printf("%+v\n",battery_And_Touch)
}