ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

go语言的结构体、指针、方法详解

2022-08-23 10:32:56  阅读:137  来源: 互联网

标签:struct int fmt 详解 func go main type 指针


资源来自: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

标签:struct,int,fmt,详解,func,go,main,type,指针
来源: https://www.cnblogs.com/ray-mr-huang/p/16615258.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有