1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        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)建指針對(duì)象

          • 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)體嵌套的實(shí)際意義

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

        • 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)體指針變量,此時(shí)變量被初始化為nil

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

        1.2 聲明并初始化

        聲明并初始化指針對(duì)象

        // 先聲明再初始化
        //var person *Person
        //person = &Person{}
        // 簡(jiǎn)短聲明
        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)建指針對(duì)象

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

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

        // 定義工廠函數(shù)用于創(chuàng)建Author對(duì)象
        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)體指針

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

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

        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的實(shí)例

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

        c1、c2c3都是car struct的實(shí)例,c2, c3是指向?qū)嵗闹羔?,指針中保存的是?shí)例的地址,所以指針再指向?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ū)嵗?。這三個(gè)變量與Car struct實(shí)例的指向關(guān)系如下

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

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

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

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

        fmt.Println("c2, ", c2.Color) // 編譯器自動(dòng)補(bǔ)充上(*c2).Color, 這樣寫法上就簡(jiǎn)潔了

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

        1.6 傳值還是傳遞指針

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

        即什么時(shí)候傳值,什么時(shí)候傳遞指針?

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

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

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

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

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

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

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

        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ù)時(shí),聲明屬于結(jié)構(gòu)體的函數(shù),方法與結(jié)構(gòu)體綁定,只能通過結(jié)構(gòu)體person的實(shí)例訪問,不能在外部直接訪問,這就是結(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 匿名嵌套

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

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

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

        // 訪問方式 結(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"
        )
        //定義手機(jī)屏幕
        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  // 是否有指紋識(shí)別
         }{
          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)體中的屬性對(duì)應(yīng)的類型也是結(jié)構(gòu)體

        給嵌入的結(jié)構(gòu)體一個(gè)名字,讓其成為另一個(gè)結(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ā)生變化,所有引用都會(huì)發(fā)生影響 使用屬性為值類型,則在復(fù)制時(shí)發(fā)生拷貝,兩者不相互影響

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

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

        示例

        package main

        import "time"

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

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

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

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

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

        即“工廠函數(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)體對(duì)外是否可見,在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)體嵌入時(shí)由被嵌入結(jié)構(gòu)體(外層)決定,被嵌入結(jié)構(gòu)體名首字母大寫時(shí)屬性名包外可見,否者只能 在包內(nèi)使用
        • 結(jié)構(gòu)體名首字母小寫,屬性名小寫:結(jié)構(gòu)體只能在包內(nèi)使用
        • 結(jié)構(gòu)體成員變量在同包內(nèi)小寫也是可以訪問到的

        總結(jié):

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

        示例:

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

        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
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美精品成人 | 欧美午夜片欧美片在线观看 | 中文字幕一区二区三区有限公司 | 亚洲乱妇 | 欧美日本精品 | 性欧美性少妇 | 国产黄色网址在线观看 | 黄色逼逼 | 一级黄色小视频 | 日韩免费高清 |