point_struct

[toc]

一.struct自整理部分

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

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.指针存取
  - 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.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.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