<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Golang數(shù)據(jù)類型之結(jié)構(gòu)體-下篇

          共 12684字,需瀏覽 26分鐘

           ·

          2021-09-01 20:56

          目錄

          • 1、結(jié)構(gòu)體指針

            • 1.1 聲明

            • 1.2 聲明并初始化

            • 1.3 通過 new 函數(shù)創(chuàng)建指針對象

            • 1.4 傳遞結(jié)構(gòu)體指針

            • 1.5 結(jié)構(gòu)體值與結(jié)構(gòu)體指針

            • 1.6 傳值還是傳遞指針

          • 2、匿名結(jié)構(gòu)體

          • 3、結(jié)構(gòu)體方法

          • 4、結(jié)構(gòu)體嵌套

            • 4.1 匿名嵌套

            • 4.2 命名嵌套

            • 4.3 指針類型結(jié)構(gòu)體嵌套

            • 4.4 結(jié)構(gòu)體嵌套的實際意義

          • 5、通過函數(shù)創(chuàng)建結(jié)構(gòu)體對象

          • 6、結(jié)構(gòu)體的可見性


          本文是 Golang數(shù)據(jù)類型之結(jié)構(gòu)體-上篇 的續(xù)篇內(nèi)容

          1、結(jié)構(gòu)體指針

          1.1 聲明

          和其他基礎(chǔ)數(shù)據(jù)類型一樣,也可聲明結(jié)構(gòu)體指針變量,此時變量被初始化為nil

          func TestMain4(t *testing.T)  {
           var person *Person
           fmt.Println(person)  // <nil>
          }

          1.2 聲明并初始化

          聲明并初始化指針對象

          // 先聲明再初始化
          //var person *Person
          //person = &Person{}
          // 簡短聲明
          person := new(Person)
          //person := &Person{}  // *Person
          fmt.Printf("%p", person)  // 0xc00013a080

          聲明并初始化賦值

          var person *Person = &Person{
              Name:          "andy",
              Age:           66,
              Gender:        "male",
              Weight:        120,
              FavoriteColor: []string{"red""blue"},
          }
          fmt.Printf("%p", person)  // 0xc0000ce080

          1.3 通過 new 函數(shù)創(chuàng)建指針對象

          Go中常定義N(n)ew+結(jié)構(gòu)體名命名的函數(shù)用于創(chuàng)建對應(yīng)的結(jié)構(gòu)體值對象或指針對象

          person := new(Person)
          fmt.Printf("%p", person)  // 0xc00013a080
          fmt.Printf("%T", person)  // *test.Person

          // 定義工廠函數(shù)用于創(chuàng)建Author對象
          func NewAuthor(id int, name, birthday, addr, tel, desc string) *User {
           return &User{id, name, birthday,addr, tel, desc}
          }
          // 調(diào)用
           me8 := NewAuthor(1004"geek""2021-06-08""北京市""15588888888""備注")
           fmt.Printf("%T: %#v\n", me8, me8)

          1.4 傳遞結(jié)構(gòu)體指針

          將一個結(jié)構(gòu)體的指針傳遞給函數(shù),能否修改到該結(jié)構(gòu)體

          結(jié)果是可以修改該實例對象

          func ChangeColor(car *Car) {
           car.Color = "blue"
           fmt.Println(car.Color)
          }

          func main() {
           car := Car{
            Color: "yellow",    // 黃色
            Brand: "ford",      // 福特
            Model: "Mustang",   // 野馬
           }
           ChangeToW(car)
           fmt.Println(car.Color) // blue
          }

          1.5 結(jié)構(gòu)體值與結(jié)構(gòu)體指針

          什么是值?什么是指針?

          下面三種方式都可以構(gòu)造Car struct的實例

          c1 := Car{}
          c2 := &Car{}
          c3 := new(Car)
          fmt.Println(c1, c2, c3) // {  } &{  } &{  }

          c1c2c3都是car struct的實例,c2, c3是指向?qū)嵗闹羔槪羔樦斜4娴氖菍嵗牡刂罚灾羔樤僦赶驅(qū)嵗?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">c1則是直接指向?qū)嵗_@三個變量與Car struct實例的指向關(guān)系如下

          變量名      指針     數(shù)據(jù)對象(實例)
          -------------------------------
          c1 -------------------> { }
          c2 -----> ptr(addr) --> { }
          c3 -----> ptr(addr) --> { }

          訪問實例和訪問實例指針是否有區(qū)別

          fmt.Println("c1, ", c1.Color)    // 訪問實例的屬性
          fmt.Println("c2, ", (*c2).Color) // 先通過*求出 指針的值,就是實例的內(nèi)存地址, 然后通過實例的內(nèi)存地址訪問該實例對象的屬性

          如果我們需要訪問指針對象的屬性, 上面的(*c2).Color是理論上的正確寫法, 可以看出過于繁瑣, 而我們方法指針,往往也是想訪問這個指針的實例, 所以編譯幫我們做了優(yōu)化, 比如訪問指針實例也可以這樣寫

          fmt.Println("c2, ", c2.Color) // 編譯器自動補充上(*c2).Color, 這樣寫法上就簡潔了

          簡單總結(jié):盡管一個是數(shù)據(jù)對象值,一個是指針,它們都是數(shù)據(jù)對象的實例。也就是說,p1.namep2.name都能訪問對應(yīng)實例的屬性,只是指針的訪問寫法是一種簡寫(正確寫法由編譯器補充)

          1.6 傳值還是傳遞指針

          前面文章 Golang函數(shù)參數(shù)的值傳遞和引用傳遞 說的也是這個話題

          即什么時候傳值,什么時候傳遞指針?

          • 傳遞值: 不希望實例被外部修改的時候,傳值就相當(dāng)于copy了一份副本給函數(shù)
          • 傳遞指針: 希望外部能修改到這個實例本身的時候,就需要傳遞該實例的指針,就是把該實例的內(nèi)存地址告訴對方,可以通過地址直接找到本體

          但是經(jīng)常看到函數(shù)接收的結(jié)構(gòu)體參數(shù)都是指針是為什么

          因為復(fù)制傳值時,如果函數(shù)的參數(shù)是一個struct對象,將直接復(fù)制整個數(shù)據(jù)結(jié)構(gòu)的副本傳遞給函數(shù),這有兩個問題

          • 函數(shù)內(nèi)部無法修改傳遞給函數(shù)的原始數(shù)據(jù)結(jié)構(gòu),它修改的只是原始數(shù)據(jù)結(jié)構(gòu)拷貝后的副本
          • 如果傳遞的原始數(shù)據(jù)結(jié)構(gòu)很大,完整地復(fù)制出一個副本開銷并不小

          所以為了節(jié)省開銷一般都會選擇傳遞指針

          2、匿名結(jié)構(gòu)體

          在定義變量時將類型指定為結(jié)構(gòu)體的結(jié)構(gòu),此時叫匿名結(jié)構(gòu)體。匿名結(jié)構(gòu)體常用于初始化一次結(jié)構(gòu)體變量的場景,例如項目配置

          package main

          import "fmt"

          func main() {
           var me struct {
            ID   int
            Name string
           }

           fmt.Printf("%T\n", me)  // struct { ID int; Name string }
           fmt.Printf("%#v\n", me)  // struct { ID int; Name string }{ID:0, Name:""}
           fmt.Println(me.ID)  // 0
           me.Name = "geek"
           fmt.Printf("%#v\n", me)  // struct { ID int; Name string }{ID:0, Name:"geek"}

           me2 := struct {
            ID   int
            Name string
           }{1"geek"}

           fmt.Printf("%#v\n", me2)  // struct { ID int; Name string }{ID:1, Name:"geek"}
          }

          3、結(jié)構(gòu)體方法

          可以為結(jié)構(gòu)體定義屬于自己的函數(shù)

          在聲明函數(shù)時,聲明屬于結(jié)構(gòu)體的函數(shù),方法與結(jié)構(gòu)體綁定,只能通過結(jié)構(gòu)體person的實例訪問,不能在外部直接訪問,這就是結(jié)構(gòu)體方法和函數(shù)的區(qū)別,例如

          // p 是person的別名
          func (p Person) add() int {
           return p.Age * 2
          }

          調(diào)用結(jié)構(gòu)體方法

          func TestMain6(t *testing.T) {
           m := new(Person)
           m.Age = 18
           fmt.Println(m.add()) // 36
          }

          4、結(jié)構(gòu)體嵌套

          4.1 匿名嵌套

          簡單來說,就是將數(shù)據(jù)結(jié)構(gòu)直接放進去,放進去的時候不進行命名

          在定義變量時將類型指定為結(jié)構(gòu)體的結(jié)構(gòu),此時叫匿名結(jié)構(gòu)體。匿名結(jié)構(gòu)體常用于初始化一次結(jié)構(gòu)體變量的場景,例如項目配置

          匿名結(jié)構(gòu)體可以組合不同類型的數(shù)據(jù),使得處理數(shù)據(jù)變得更為靈活。尤其是在一些需要將多個變量、類型數(shù)據(jù)組合應(yīng)用的場景,匿名結(jié)構(gòu)體是一個不錯的選擇

          // 訪問方式 結(jié)構(gòu)體.成員名
          type Person2 struct {
           Name          string
           Age           int
           Gender        string
           Weight        uint
           FavoriteColor []string
           NewAttr       string
           Addr          Home
           NewHome
          }

          type NewHome struct {
           City string
          }

          func TestPerson2(t *testing.T) {
           m := new(Person2)
           m.Age = 18
           m.City = "beijing"
           fmt.Println(m.City)  // beijing
          }

          嵌套過后帶來的好處就是能夠像訪問原生屬性一樣訪問嵌套的屬性

          示例

          package main

          import (
           "encoding/json"
           "fmt"
          )
          //定義手機屏幕
          type Screen01 struct {
           Size       float64 //屏幕尺寸
           ResX, ResY int //屏幕分辨率 水平 垂直
          }
          //定義電池容量
          type Battery struct {
           Capacity string
          }

          //返回json數(shù)據(jù)
          func getJsonData() []byte {
           //tempData 接收匿名結(jié)構(gòu)體(匿名結(jié)構(gòu)體使得數(shù)據(jù)的結(jié)構(gòu)更加靈活)
           tempData := struct {
            Screen01
            Battery
            HashTouchId bool  // 是否有指紋識別
           }{
            Screen01:    Screen01{Size: 12, ResX: 36, ResY: 36},
            Battery:     Battery{"6000毫安"},
            HashTouchId: true,
           }
           jsonData, _ := json.Marshal(tempData)  //將數(shù)據(jù)轉(zhuǎn)換為json
           return jsonData
          }

          4.2 命名嵌套

          結(jié)構(gòu)體命名嵌入是指結(jié)構(gòu)體中的屬性對應(yīng)的類型也是結(jié)構(gòu)體

          給嵌入的結(jié)構(gòu)體一個名字,讓其成為另一個結(jié)構(gòu)體的屬性

          適用于復(fù)合數(shù)據(jù)結(jié)構(gòu)<嵌入匿名>

          嵌套定義

          type Book struct {
              Author  struct{
                  Name string
                  Aage int
              }
              Title struct{
                  Main string
                  Sub  string
              }
          }

          聲明和初始化

          b := &Book{
              Author: struct {
                  Name string
                  Aage int
              }{
                  Name: "xxxx",
                  Aage: 11,
              },
              Title: struct {
                  Main string
                  Sub  string
              }{
                  Main: "xxx",
                  Sub:  "yyy",
              },
          }

          //
          b := new(Book)
          b.Author.Aage = 11
          b.Author.Name = "xxx"

          嵌入命名,在外面定義

          type Author struct {
              Name string
              Aage int
          }

          type Title struct {
              Main string
              Sub  string
          }

          type Book struct {
              Author Author
              Title Title
          }

          示例

          package main

          import "fmt"

          type Person struct {
           Name string
           Age  int
          }

          type TeacherNew struct {
           Pn        Person
           TeacherId int
          }

          func main() {
           t2 := TeacherNew{
            Pn: Person{
             Name: "geek",
             Age:  18,
            },
            TeacherId: 123,
           }
           fmt.Printf("[TeacherId: %v][Name: %v][Age: %v]", t2.TeacherId, t2.Pn.Name, t2.Pn.Age)
            // [TeacherId: 123][Name: geek][Age: 18]
          }

          4.3 指針類型結(jié)構(gòu)體嵌套

          結(jié)構(gòu)體嵌套(命名&匿名)類型也可以為結(jié)構(gòu)體指針

          聲明&初始化&操作

          type Book2 struct {
           Author *Author
           Title *Title
          }

          func (b *Book2) GetName() string {
           return b.Author.GetName() + "book"
          }

          func TestMain8(t *testing.T) {
           b1 := Book2{
            Author: &Author{
             Name: "ssgeek",
            },
            Title: &Title{},
           }

           b2 := &Book2{
            Author: &Author{},
            Title: &Title{},
           }
          }

          使用屬性為指針類型底層共享數(shù)據(jù)結(jié)構(gòu),當(dāng)?shù)讓訑?shù)據(jù)發(fā)生變化,所有引用都會發(fā)生影響 使用屬性為值類型,則在復(fù)制時發(fā)生拷貝,兩者不相互影響

          4.4 結(jié)構(gòu)體嵌套的實際意義

          • 例如大項目對應(yīng)復(fù)雜的配置文件,將公共的字段抽取出來,放到一個公共common的結(jié)構(gòu)體
          • cmdb、資產(chǎn)系統(tǒng)等類型設(shè)計

          示例

          package main

          import "time"

          // 云有云資源公共字段
          type Common struct {
           ChargingMod string    // 付費模式:預(yù)付費和后付費
           Region      string    // 區(qū)域
           Az          string    // 可用區(qū)
           CreateTime  time.Time // 購買時間
          }

          type Ecs struct {
           Common
           guide string // 4C 16G
          }

          type Rds struct {
           Common
           dbType string // 代表數(shù)據(jù)庫是哪一種
          }

          5、通過函數(shù)創(chuàng)建結(jié)構(gòu)體對象

          除了通過直接賦值創(chuàng)建結(jié)構(gòu)體對象,還可以通過函數(shù)來創(chuàng)建,也就是把創(chuàng)建結(jié)構(gòu)體對象的過程進行封裝

          即“工廠函數(shù)”

          package main

          import "fmt"

          type Address struct {
           Region string
           Street string
           No     string
          }

          type User struct {
           ID   int
           Name string
           Addr *Address
          }

          func NewUser(id int, name string, region, street, no string) *User {
           return &User{
            ID:   id,
            Name: name,
            Addr: &Address{region, street, no},
           }
          }

          func main() {
           me := User{
            ID:   1,
            Name: "geek",
            Addr: &Address{"上海市""南京路""0001"},
           }

           me2 := me
           me2.Name = "ss"
           me2.Addr.Street = "黃河路"

           fmt.Printf("%#v\n", me.Addr)
           fmt.Printf("%#v\n", me2.Addr)

           hh := NewUser(2"hh""北京市""海淀路""0001")
           fmt.Printf("%#v\n", hh)
          }

          6、結(jié)構(gòu)體的可見性

          結(jié)構(gòu)體對外是否可見,在go中受其首字母是否大寫控制,結(jié)論是

          結(jié)構(gòu)體首字母大寫則包外可見(公開的),否者僅包內(nèi)可訪問(內(nèi)部的) 結(jié)構(gòu)體屬性名首字母大寫包外可見(公開的),否者僅包內(nèi)可訪問(內(nèi)部的)

          組合起來的可能情況:

          • 結(jié)構(gòu)體名首字母大寫,屬性名大寫:結(jié)構(gòu)體可在包外使用,且訪問其大寫的屬性名
          • 結(jié)構(gòu)體名首字母大寫,屬性名小寫:結(jié)構(gòu)體可在包外使用,且不能訪問其小寫的屬性名
          • 結(jié)構(gòu)體名首字母小寫,屬性名大寫:結(jié)構(gòu)體只能在包內(nèi)使用,屬性訪問在結(jié)構(gòu)體嵌入時由被嵌入結(jié)構(gòu)體(外層)決定,被嵌入結(jié)構(gòu)體名首字母大寫時屬性名包外可見,否者只能 在包內(nèi)使用
          • 結(jié)構(gòu)體名首字母小寫,屬性名小寫:結(jié)構(gòu)體只能在包內(nèi)使用
          • 結(jié)構(gòu)體成員變量在同包內(nèi)小寫也是可以訪問到的

          總結(jié):

          • 跨包訪問:全局變量、結(jié)構(gòu)體本身、結(jié)構(gòu)體成員變量、必須要首字母大寫才可以暴露出來被訪問到(在 go 中常見的是會給結(jié)構(gòu)體綁定一個方法,返回小寫的成員變量讓外面訪問到)
          • 同包訪問:上述變量首字母小寫也可以被訪問到

          示例:

          首先在tt包下定義一個person結(jié)構(gòu)體,person大寫的時候外部的包可以訪問到,person小寫的時候外部的包不可以訪問到

          package main

          import (
           "fmt"
           "go-learning/chapter06/tt"
          )

          func main() {
           p1 := tt.Person{
            Name: "geek",
            Age:  18,
           }
           fmt.Println(p1)
           /*
           # command-line-arguments
           ./last.go:9:8: cannot refer to unexported name tt.person
            */

          }

          See you ~

          瀏覽 50
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产又粗又长又大在线免费观看 | 色老板精品永久免费视频 | 色秘 乱码一区二区三区在线男奴 | 免费观看黄色a片 | 成人视频免费观看18 |