首页 > 其他分享 >go语言的结构体、指针、方法详解

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

时间:2022-08-23 10:36:30浏览次数:83  
标签: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,指针
From: https://www.cnblogs.com/ray-mr-huang/p/16615258.html

相关文章

  • go build 成不同环境的方法:windows、mac、linux
    修改goenv环境变量,如下示例:1、Windows下编译Mac,Linux平台的64位可执行程序:$goenv-wCGO_ENABLED=0GOOS=darwin3GOARCH=amd64$goenv-wCGO_ENABLED=0GOOS=li......
  • 【Go实战基础】数组实战,程序员的基本功
     数组实战,程序员的基本功。实战需求:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。实战思路:......
  • go语言并发-02channel
    go语言通道channel如果说goroutine是Go语言程序的并发体的话,那么channels就是它们之间的通信机制。一个channels是一个通信机制,它可以让一个goroutine通过它给......
  • mongodb4.4 Aggregation
    如果需要进行数据分析,那么可以使用MongoDB的聚合框架,可以对一个或多个集合中的文档进行分析。聚合框架基于管道的概念。使用聚合管道可以从MongoDB集合获取输入,并将该集......
  • (转载)Linux目录详解,软件应该安装到哪个目录
    Linux目录详解,软件应该安装到哪个目录我们应该知道Windows有一个默认的安装目录专门用来安装软件。Linux的软件安装目录也应该是有讲究的,遵循这一点,对后期的管理和维......
  • [Google] LeetCode 1610 Maximum Number of Visible Points 极角排序
    Youaregivenanarraypoints,anintegerangle,andyourlocation,wherelocation=[posx,posy]andpoints[i]=[xi,yi]bothdenoteintegralcoordinateson......
  • 慎用django orm的update_or_create方法
    根据错误日志,发现产生死锁的有4个接口。这4个接口中,阅读业务代码,发现均有使用update_or_create。为什么update_or_create方法会造成死锁呢?通过阅读源码   发现,upd......
  • beego commentsRouter.go不能自动生成
    beego2.0开始使用注解路由,然而请求一直404发现是少了routers/commentsRouter.go官方文档https://beego.vip/docs/mvc/controller/router.md但并未说明还可以通过......
  • 彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris项目实战-模板与数据库EP02
    书接上回,上次我们搭建好了项目入口文件,同时配置了路由体系,接着就可以配置项目的模板了,这里我们采用Iris内置的模板引擎,事实上,采用模板引擎并不意味着前后端耦合,模板中的数......
  • c++ 智能指针
    智能指针,是模板类,意在避免在使用动态内存时,出现异常等意外,或忘记使用delete,而造成内存泄漏。这个智能指针,在指针变量结束声明周期后,调用对象的析构函数,并自动去释放这个指......