Go语言的使用结构体、指针和方法

结体体定义如下:

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)}

  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页
评论

打赏作者

DXB2021

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值