point_struct

[toc]

一.struct自整理部分

(一).struct核心基础知识点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
1.在一个struct中,字段名字必须唯一。
2.如果字段在代码中从不会用到,可以命名为_(空标识符号)。
3.struct是值类型,使用new(structName)来给该结构体变量分配内存。
它返回指向已分配内存的指针。

type Teacher struct {
name string
age uint8
sex string
}

var t Teacher // t是结构体类型变量
var tp *Teacher // tp是指向一个结构体类型变量的指针

4.结构体初始化方式:
t2 := &Teacher{} // 这种写法依旧会调用new()初始化。
t3 := new(Teacher)

5.带tag的struct
package main

import (
"fmt"
"reflect"
)

type Tag struct {
goodsNumber bool "是否有存货"
goodsName string "商品名称"
goodsPrice int "商品价格"
}


// 这里的标签内容只有reflec可以获取
func getTagFromReflect(TagType Tag,Index int){
getTagType := reflect.TypeOf(TagType)
indexField := getTagType.Field(Index)
fmt.Printf("%v\n",indexField.Tag)
}

func main() {
t := Tag{true, "apple", 130}
for i := 0; i < 3; i++ {
getTagFromReflect(t, i)
}
}

6.在一个struct中每一种数据类型只能有一个匿名字段。
7.案例
// 定义struct实例
type Employee struct {
Id string
Name string
Age int
}
// 实例创建以及初始化
e := Employee{"0", "Bob", 20}
e1 := Employee{Name: "Mike", Age: 30}
e2 := new(Employee) //注意这⾥里里返回的引⽤用/指针,相当于 e := &Employee{}
e2.Id = "2" //与其他主要编程语⾔言的差异:通过实例例的指针访问成员不不需要使⽤用-> e2.Age = 22
e2.Name = "Rose"

// 行为(方法)的定义
//第一种定义⽅方式在实例例对应⽅方法被调⽤用时,实例例的成员会进⾏行行值复制
func (e Employee) String() string {
return fmt.Sprintf("ID:%s-Name:%s-Age:%d", e.Id, e.Name, e.Age)
}

//通常情况下为了了避免内存拷⻉贝我们使⽤用第二种定义⽅方式
func (e *Employee) String() string {
return fmt.Sprintf("ID:%s/Name:%s/Age:%d", e.Id, e.Name, e.Age)
}
8.接口方法结构体应用案例
package jesse_test

import (
"fmt"
"testing"
)

type Car interface {
Run() string
}

type BMW struct {
Name string
}

func (b *BMW) Run() string {
return fmt.Sprintf("我是一辆%v车,我跑的很开心\n", b.Name)
}

type Tesla struct {
Name string
}

func (t *Tesla) Run() string {
return fmt.Sprintf("我是一辆%v车,我跑的很开心\n", t.Name)
}

func AutoRun(c Car) {
fmt.Printf("我的类型是%T,我是:%v\n", c, c.Run())
}

func TestCar(t *testing.T) {
//var i Car
//i = &BMW{"宝马"}
//fmt.Println(i.Run())

//startTime := time.Now()
//测试二
bmwCar := &BMW{"宝马"}
AutoRun(bmwCar)

//teslaCar := new(Tesla)
teslaCar := &Tesla{"Tesla"}
AutoRun(teslaCar)
//time.Sleep(100)
//fmt.Println(time.Since(startTime).Seconds())
}

二.指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
1.指针存取
- a = 10 b=&a // 相当于取存储a的盒子地址。
- &a &b 的类型为指针。
- &的作用为获取变量的地址。
- *int表示变量的类型为整数的指针。
- 指针类型不能做加减。

2.关联案例代码

main.go
#----------------------------------------------------------------------------
package main

import "fmt"

func add(a,b *int){
*a+=*b
}

func put(m map[string]string){
m["a"]="AAA"
}

func main() {
a,b := 1,2
add(&a,&b)
fmt.Println(a)
// a=3,b=2

c := &a // *int c指向a的盒子,*c 取出a里面存的东西3
d := &c // **int d指向c的盒子,d本身也是指针,它存的也是指针。
fmt.Println("d =",d,"*d =",*d,"**d =",**d)

m := map[string]string{}
mp1 := &m // mp1的类型是 *map[string]string
fmt.Println("mp1:",mp1)
put(m)
fmt.Println("第一次put m:",m)
fmt.Println("*mp1=",*mp1)


f1 := add // f1 = func(int,int)
f1(&a,&b)
fmt.Println("第一次打印:",a) // a=5
f2 := &f1
(*f2)(&a,&b)
fmt.Println("第二次打印:",a) // a=7

// 空指针 未初始化为nil
{
var nothing *int
nothing = new(int) // 这里是我完善的。
*nothing = 3
fmt.Println("*nothing", *nothing)
}

// 测试未初始化的map,初始值也是nil
{
var nothing map[string]string = make(map[string]string)
nothing["a"]="mapa"
nothing["a1"]="mapa"
nothing["a2"]="mapa"
nothing["a3"]="mapa"
fmt.Println("空map:",nothing)
}

// 测试空slice
{
var nothingSlice []int
nothingSlice = append(nothingSlice,1)
nothingSlice[0]=100 // 但是这里可以直接append操作
fmt.Println("slice:",nothingSlice)
}
}
#----------------------------------------------------------------------------

三.Go面向对象编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
1.Go语言支持面向对象编程(OOP,Object-orientedprogramming)。虽然Go语言不是严格的面向对象编程语言。
2.Go语言中的结构体(struct)与其它语言中的类(class)在面向对象编程中处于同等地位。
3.Go语言中没有严格的继承、方法重载、构造函数等。
4.Go语言通过接口(interface)完成面向对象编程。

5.对象实战案例
#----------------------------------------------------------------------------
package main

import "fmt"

// 声明一个对象 对象的体现方式为struct

type Person struct {
Name string
Sex string
Tall float64
Weight float64
Age int
}

func inputPeopleInfo() {

// person是一个slice,类型是一个struct
person := []Person{
{
"jesse",
"男",
float64(1.75),
float64(70),
27,
},
{
"jesse1",
"男1",
float64(1.76),
float64(71),
28,
},
}

for _,items := range person{
fmt.Println(items)
}
}

func main() {
inputPeopleInfo()
}
#----------------------------------------------------------------------------

6.为什么要有struct
当一组数据总是围绕着某个主体时,表示这些数据与这个主体之间有紧密的关联关系,可将该主体提
取为一个对象,围绕着主体的数据为这个对象的属性,围绕着这些数据的操作即为该主体的成员函数。
Go 语言中,对象的体现方式为结构体(struct)。

7.怎么定义结构体变量?
var <变量名> [*]<变量类型>
var tom Person
var tom *Person
<变量名> := [&]<变量类型>{}
tom := Person{}
tom := &Person{}
<变量名> := [&]<变量类型>{[ [属性名]:[属性值] ]}
tom := &Person{"Tom”, 23}
tom := &Person{Name: "Tom”, Age: 23}
var <变量名> *<变量类型> = new(Person)
var tom *Person = new(Person)
8.结构体的操作
- 结构体的属性在同一个包内均可见
- 只有公有的结构体、成员变量、成员函数在包外部可见
- 结构体的成员函数执行时,只能通过结构体指针的成员函数进行更改

9.结构体的嵌套
- 结构体的嵌套是指直接嵌入其他结构体完成结构体的定义。
- 被嵌入的结构体的所有成员变量、成员方法都可以直接被使用。
- 注意:
- 被嵌入的结构体的成员变量、成员函数也遵循公有、私有访问限制。
- 在创建变量时,嵌入的对象也需要实例化,尤其是引用类型。如:指针类型、Map。

10.结构体的嵌套实例化一
#----------------------------------------------------------------------------
package calc

type Calculator struct {
Left, Right int
Result int
}

type NewCalculator struct {
old Calculator
}

func getNewCalculator() *NewCalculator {
return &NewCalculator{} //old自动被实例化
}

type NewCalculator2 struct {
old *Calculator
}

func getNewCalculator2() *NewCalculator2 {
return &NewCalculator2{old: &Calculator{}}
//old必须实例化,否则会空指针错误
}
#----------------------------------------------------------------------------

11.结构体的嵌套实例化二
#----------------------------------------------------------------------------
package main

import (
"fmt"
)

type Calculator struct {
Left, Right int
//OP string
Result int
}

// 测试继承
type NewCalculator struct {
Calculator
}

// 测试传入引用类型
type PointCalcularor struct {
old *Calculator
}

// 方法
func (c *Calculator) Add() int {
tmpResult := c.Left + c.Right
c.Result = tmpResult
fmt.Printf("c.result = %v\n",c.Result)
return tmpResult
}

func (c *Calculator) Sub() int {
return c.Left - c.Right
}

func (c *Calculator) Multiple() int {
return c.Left * c.Right
}

func (c *Calculator) Divide() int {
return c.Left / c.Right
}

func (c *Calculator) Remainder() int {
return c.Left % c.Right
}

func calcFunc() {

//fmt.Scanln(&Left)
//fmt.Scanln(&Right)
//fmt.Scanln(&OP)

//switch op {
//case "+":
//case "-":
//case "*":
//case "/":
//case "%":
//default:
// fmt.Println("Not supported calculator rule.")
//}

var c = &Calculator{
Left: 1,
Right: 11,
}

fmt.Println(c.Add())

// 测试继承
newC := &NewCalculator{}
newC.Left = 999
newC.Right = 888
fmt.Println(newC.Add())

//测试继承的是引用类型 嵌入的对象也需要实例化 负责会抛异常。
pointC := &PointCalcularor{old:&Calculator{
Left: 999,
Right: 876,
}}

//pointC.old.Left =200
//pointC.old.Right=1
fmt.Println(pointC.old.Add())
fmt.Println("pointC:",pointC.old.Add())
}


// 结构体里面定义的属性如果是引用类型 需要进行初始化 否则会抛出异常。
type MyCommand struct {
mainCommand *string
commandOptions map[string]string
}

func (m *MyCommand) ToCmdStr() string {

out := ""
for k,v := range m.commandOptions{
out = out + fmt.Sprintf("--%s=%s",k,v)
}
return out
}

func testCmd(){
mc := &MyCommand{
new(string),
make(map[string]string),
}

mc.commandOptions["author"]="jesse"
*mc.mainCommand="myPointType"
fmt.Println(*mc.mainCommand)
fmt.Println(mc.ToCmdStr())
}


func main() {
//calcFunc()
testCmd()
}
#----------------------------------------------------------------------------

12.sort.Slice测试案例

main.go
#----------------------------------------------------------------------------
package main

import (
"fmt"
"sort"
)

type Peopel struct {
Name string
Age int
}

func InitPeople()(arrs []Peopel){
//arr1 :=Peopel{
// Name: "jesse",
// Age: 23,
//}
//arr2 :=Peopel{
// Name: "maggie",
// Age: 24,
//}
//arr3 :=Peopel{
// Name: "gina",
// Age: 25,
//}
//arr4 :=Peopel{
// Name: "jack",
// Age: 26,
//}
//
//arrs = append(arrs,arr1,arr2,arr3,arr4)
//return arrs

arrs = []Peopel{
{
"jesse",
23,
},
{
"maggie",
21,
},
{
"gina",
29,
},
{
"rainbow",
31,
},
}
return
}

func main(){
arrs := InitPeople()
fmt.Println("before:",arrs)
sort.Slice(arrs, func(i, j int) bool {
// 从小到大排列
return arrs[i].Age < arrs[j].Age
})
fmt.Println("按照年龄 从小到大排列:",arrs)
#----------------------------------------------------------------------------

四.面向对象的特殊类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
1.prometheus的一个核心引擎的接口定义.
- https://github.com/prometheus/prometheus/blob/main/promql/engine.go
2.类型重命名
- 为已有的类型创建一个新的别名,从而在不更改类型的情况下提高类型的辨识度。
- type 新名称 = 要命名的名称/ type Math = int

3.投票小游戏
- 班里100个人,现在我们选出1个班长,一个副班长。
- 每个人有两张票,一张赞成票,一张反对票,每个人根据自己对其他99个人的了解来头哦票。
- 得赞成票最多的是班长,赞成票第二多的是副班长,如果赞成票相同,得反对票少的当选。

#----------------------------------------------------------------------------
package main

import (
"fmt"
"math/rand"
"time"
)

type Math = int
type English = int
type Chinese = int

func getScoreOfStudent(name string) (Math, Chinese, English) {
//todo
return 1, 2, 3
}

func callGetScoreOfStudent() {
var mathScore int = 100
var mathScore2 Math = 101

mathScore = mathScore2
fmt.Println(mathScore)
math, chinese, english := getScoreOfStudent("jesse")
fmt.Printf("math:%v,chinese:%v,english:%v\n", math, chinese, english)
}

// 投票小游戏

type VoteGame struct {
students []*Student
}

func (v *VoteGame)goRun()(leader *Student) {
for _,item :=range v.students{
rand.Seed(time.Now().UnixNano())
randInt := rand.Int()
if randInt %2 ==0 {
item.voteA(v.students[randInt%len(v.students)]) // todo 使用随机数代替
}else {
item.voteA(v.students[randInt%len(v.students)])
}
}

maxScore := -1
maxScoreIndex := -1
for i,item := range v.students{
if maxScore < item.agree{
maxScore = item.agree
maxScoreIndex = i
}
}

if maxScoreIndex >=0 { // 判断是否大于0,如果没有学生,那么index就是默认值-1.
return v.students[maxScoreIndex]
}
return nil
}


type Student struct {
name string
agree int
disagree int
}

func (std *Student) voteA(target *Student) {
target.agree++

}

func (std *Student) voteD(target *Student) {
target.disagree++

}

func main() {
vg := &VoteGame{students: []*Student{
&Student{name:fmt.Sprintf("%d",1)},
&Student{name:fmt.Sprintf("%d",2)},
&Student{name:fmt.Sprintf("%d",3)},
&Student{name:fmt.Sprintf("%d",4)},
&Student{name:fmt.Sprintf("%d",5)},
}}

leader := vg.goRun()
fmt.Println(leader)
}
#----------------------------------------------------------------------------

4.对象重定义
- 对象重定义是将对象作为新的对象存在。新对象可以有单独的成员函数以支持不同的操作,或实
现不同的接口。原对象与新对象之间可以无缝转换,但不能直接混用。
语法:
type <新对象类型名称> [*]<原对象类型名称> 例如:
type ClassPresident Person
type Name string
type ConvInt int