Go 第10章 结构体 Go 第10章 结构体

2021-06-09

一、结构体

  • 结构体是一个可以存储不同数据类型的数据类型,因为在声明完以后,它可以和数据类型一样使用, 也有指针、值等等

  • go 语言仅支持封装,不支持继承和多态

  • go 语言没有 class,只有 struct

  • 声明和使用

package main

import "fmt"

type Human struct {
   Name  string
   Age   int
   Sex   bool
   Hobby []string
}

func main() {
   var human Human
   human.Name = "lulu"
   human.Age = 18
   human.Sex = true
   human.Hobby = []string{"踢球", "玩游戏"}
   fmt.Println(human)

   human2 := Human{
      Name:  "lulu",
      Age:   18,
      Sex:   true,
      Hobby: []string{"踢球", "玩游戏"},
   }
   fmt.Println(human2)

   human3 := Human{"lulu", 18, true, []string{"踢球", "玩游戏"}}
   fmt.Println(human3)

   var human4 Human
   fmt.Println(human4)

   human5 := new(Human)
   fmt.Println(human5)
}
  • 访问参数

package main

import "fmt"

type Human struct {
   Name  string
   Age   int
   Sex   bool
   Hobby []string
}

func main() {
   var human Human
   human.Name = "lulu"
   human.Age = 18
   human.Sex = true
   human.Hobby = []string{"踢球", "玩游戏"}
   fmt.Println(human.Name)
}
  • 作为方法参数

package main

import "fmt"

type Human struct {
   Name  string
   Age   int
   Sex   bool
   Hobby []string
}

func main() {
   human := Human{
      Name:  "lulu",
      Age:   18,
      Sex:   true,
      Hobby: []string{"踢球", "玩游戏"},
   }
   humanFunc(human)
}

func humanFunc(hm Human) {
   fmt.Println(hm)
}
  • 结构体指针

package main

import "fmt"

type Human struct {
   Name  string
   Age   int
   Sex   bool
   Hobby []string
}

func main() {
   human := Human{
      Name:  "lulu",
      Age:   18,
      Sex:   true,
      Hobby: []string{"踢球", "玩游戏"},
   }
   fmt.Println(human.Age)
   var humanP *Human
   fmt.Println(humanP)
   humanP = &human
   fmt.Println(humanP)
   humanP.Age = 20
   fmt.Println(human.Age)
   (*humanP).Age = 21
   fmt.Println(human.Age)
}
  • 结构体方法

package main

import "fmt"

type Human struct {
   Name  string
   Age   int
   Sex   bool
   Hobby []string
}

func (hm *Human) play(name string) (res string) {
   res = "他是国服鲁班"
   fmt.Printf("%v玩%v很厉害", hm.Name, name)
   return res
}

func main() {
   human := Human{
      Name:  "lulu",
      Age:   18,
      Sex:   true,
      Hobby: []string{"踢球", "玩游戏"},
   }
   res := human.play("王者荣耀")
   fmt.Println("\n", res)
}
  • 结构体嵌套结构体

package main

import "fmt"

type Human struct {
   Name  string
   Age   int
   Sex   bool
   Hobby []string
   Address
   Address2 Address
}

type Address struct {
   City string
}

func main() {
   human := Human{
      Name:  "lulu",
      Age:   18,
      Sex:   true,
      Hobby: []string{"踢球", "玩游戏"},
   }
   human.Address.City = "深圳"
   human.Address2.City = "广州"
   fmt.Println(human)
   fmt.Println(human.City)
   fmt.Println(human.Address2.City)
}
  • 二叉树

package main

import "fmt"

type treeNode struct {
   value       int
   left, right *treeNode
}

// 为结构定义方法
func (node treeNode) print() {
   fmt.Print(node.value, " ")
}

// 使用指针作为方法接收者
func (node *treeNode) setValue(value int) {
   if node == nil {
      fmt.Println("Setting value to nil node. Ignored. ")
      return
   }
   node.value = value
}

// 遍历树
func (node *treeNode) traverse() {
   if node == nil {
      return
   }
   node.left.traverse()
   node.print()
   node.right.traverse()
}
func createTreeNode(value int) *treeNode {
   return &treeNode{value: value}
}
func main() {
   var root treeNode
   root = treeNode{value: 3}
   root.left = &treeNode{}
   root.right = &treeNode{5, nil, nil}
   root.right.left = new(treeNode)
   root.left.right = createTreeNode(2)

   //遍历树
   root.traverse()
   fmt.Println()

   //为结构定义方法
   root.right.left.print()
   root.right.left.setValue(4)
   root.right.left.print()
   fmt.Println()
   root.print()
   root.setValue(100)
   root.print()
   fmt.Println()

   pRoot := &root
   pRoot.print()
   pRoot.setValue(200)
   pRoot.print()
   fmt.Println()

   //nil 指针也可以调用方法!
   var qRoot *treeNode
   qRoot.setValue(200)
   qRoot = &root
   qRoot.setValue(300)
   qRoot.print()
   fmt.Println()
}

https://file.lulublog.cn/images/3/2022/08/B7A997PuVp9a9vp5a5g57RPA9apjg5.png

  • 值接收者 vs 指针接收者

    • 要改变内容必须使用指针接收者

    • 结构过大也考虑使用指针接收者

    • 一致性︰如有指针接收者,最好都是指针接收者

    • 值接收者是 go 语言特有

    • 值/指针接收者均可接收值/指针

二、包和封装

2.1、封装

  • 名字一般使用 CamelCase

  • 首字母大写:public

  • 首字母小写:private

2.2、包

2.2.1、注意事项

  • 每个目录一个包

  • main 包包含可执行入口

  • 为结构定义的方法必须放在同一个包内

  • 可以是不同文件

2.2.2、示例

  • 文件目录

https://file.lulublog.cn/images/3/2022/08/Uc66v8ez807qIrKXTIQ6d6zIXc3C6R.png

  • tree.go

package tree

import "fmt"

type Node struct {
    Value   int
    Left, Right *Node
}

func CreateNode(value int) *Node {
    return &Node{Value: value}
}

func (node *Node) SetValue(value int) {
    if node == nil {
        fmt.Println("Setting value to nil node. Ignored. ")
        return
    }
    node.Value = value
}

func (node Node) Print() {
    fmt.Print(node.Value, " ")
}
  • traverse.go

package tree

func (node *Node) Traverse( ) {
    if node == nil{
        return
    }
    node.Left.Traverse( )
    node.Print( )
    node.Right.Traverse( )
}
  • entry.go

package main
import (
    "fmt"
    "learn/tree"
)
func main( ) {
    var root tree.Node
    
    root = tree.Node{Value: 3}
    root.Left = &tree.Node{}
    root.Right = &tree.Node{5, nil, nil}
    root.Right.Left = new(tree.Node)
    root.Left.Right = tree.CreateNode(2)
    
    root.SetValue(100)
    root.Traverse()
    
    fmt.Println()
}
  • 执行

go run /mnt/go/src/learn/tree/entry/entry.go
  • 执行结果

0 2 100 0 5

三、扩展已有类型

3.1、定义别名

  • entry.go

package main
import (
   "fmt"
   "learn/tree"
)

type myTreeNode struct {
   node *tree.Node
}

func (myNode *myTreeNode) postOrder() {
   if myNode == nil || myNode.node == nil {
       return
   }
   left := myTreeNode{myNode.node.Left}
   right := myTreeNode{myNode.node.Right}
   
   left.postOrder()
   right.postOrder()
   
   myNode.node.Print()
}

func main( ) {
   var root tree.Node
   
   root = tree.Node{Value: 3}
   root.Left = &tree.Node{}
   root.Right = &tree.Node{5, nil, nil}
   root.Right.Left = new(tree.Node)
   root.Left.Right = tree.CreateNode(2)
   
   root.SetValue(100)
   root.Traverse()
   
   fmt.Println()
   
   myRoot := myTreeNode{&root}
   myRoot.postOrder()
   
   fmt.Println()
}

3.2、使用组合

  • queue.go

package queue

type Queue []int

func (q *Queue) Push(v int) {
    *q = append(*q, v)
}

func (q *Queue) Pop() int {
    head := (*q)[0]
    *q = (*q)[1:]
    return head
}

func (q *Queue) IsEmpty() bool {
    return len(*q) == 0
}

  • entry.go

package main

import (
    "fmt"
    "learn/queue"
)

func main() {
    q := queue.Queue{1}
    q.Push(2)
    q.Push(3)
    
    fmt.Println(q.Pop())
    fmt.Println(q.Pop())
    fmt.Println(q.IsEmpty())
    
    fmt.Println(q.Pop())
    fmt.Println(q.IsEmpty())
}

  • 代码目录

https://file.lulublog.cn/images/3/2022/08/gp7307M6L6wIiiMAPVWol7v6V6M7pA.png

  • 运行结果

1
2
false
3
true

四、GOPATH以及目录结构

4.1、注意事项

  • 默认在~/go(unix, linux),%USERPROFILE%\go (windows)

  • 官方推荐∶所有项目和第三方库都放在同一个 GOPATH 下

  • 也可以将每个项目放在不同的 GOPATH

  • 获取 GOPATH 目录

echo $GOPATH
  • 设置 GOPATH

vim /etc/profile

export GOROOT=/usr/local/go #设置为go安装的路径
export GOPATH=/mnt/go #go项目路径
export PATH=$PATH:$GOPATH/bin:$GOROOT/bin #以冒号分隔

source /etc/profile
  • GOPATH 下的目录结构

https://file.lulublog.cn/images/3/2022/08/AGOG4pzBpgF94FPnir7rB7yNoiOav2.png

4.2、go get 获取第三方库

  • 使用 gopm 来获取无法下载的包

go get -v github.com/gpmgo/gopm

执行后之后会在 GOPATH 目录生成如下文件

https://file.lulublog.cn/images/3/2022/08/LmWwlNN6jG79oh2m2pWp0JPYYPhPG2.png

https://file.lulublog.cn/images/3/2022/08/et59P6Z1150SsyYTl5Ph1ATm595Aap.png

  • gopm 帮助

gopm help get

https://file.lulublog.cn/images/3/2022/08/f9eEG9Qc9zEmdE09eMem9t0hIegGHh.png

  • 安装 goimports

gopm get -g -v -u golang.org/x/tools/cmd/goimports
  • go build来编译

  • go install 产生pkg 文件和可执行文件

cd /mnt/go/src/learn/tree
go intsall

https://file.lulublog.cn/images/3/2022/08/ayfP0m2gMwgN2r2E0z9eq5urn9r3gA.jpg

  • go run 直接编译运行

阅读 1111