Golang 入門 : 結構體(struct)


Go 通過類型別名(alias types)和結構體的形式支持用戶自定義類型,或者叫定制類型。試圖表示一個現實世界中的實體。

結構體由一系列命名的元素組成,這些元素又被稱為字段,每個字段都有一個名稱和一個類型。
結構體的目的就是把數據聚集在一起,以便能夠更加便捷地操作這些數據。結構體的概念在 C 語言里很常見,被稱為 struct。Golang 中的結構體也是 struct。Go 語言中沒有類的概念,因此在 Go 中結構體有着更為重要的地位。結構體是復合類型(composite types),當需要定義一個類型,它由一系列屬性組成,每個屬性都有自己的類型和值的時候,就應該使用結構體,它把數據聚集在一起。然后可以訪問這些數據,就好像它是一個獨立實體的一部分。結構體也是值類型,因此可以通過 new 函數來創建。

定義結構體

在 Golang 中最常用的方法是使用關鍵字 type 和 struct 來定義一個結構體,以關鍵字 type 開始,之后是新類型的名字,最后是關鍵字 struct:

// Person 為用戶定義的一個類型
type Person struct {
    Name  string
    Age     int
    Email string
}

還有一些簡單的寫法,比如:

type T struct { a, b int }

也是合法的,它更適用於簡單的結構體。

結構體里的字段都有名字,比如上面例子中的 Name、Age 和 Email 等等。如果一個字段在代碼中從來不會被用到,那可以把它命名為 _,即空標識符。

結構體中的字段可以是任何類型,甚至是結構體本身,也可以是函數或者接口。可以聲明結構體類型的一個變量,然后像下面這樣給它的字段賦值:

var p Person
p.Name = "nick"
p.Age = 28

另外,數組可以看作是一種結構體類型,不過它使用下標而不是具名的字段。

字段標記
在定義結構體時還可以為字段指定一個標記信息:

type Person struct {
    Name  string `json:"name"`
    Age     int         `json:"age"`
    Email string `json:"email"`
}

這些標記信息通過反射接口可見,並參與結構體的類型標識,但在其他情況下被忽略。

聲明結構體類型的變量

一旦定義了結構體類型就可以使用這個類型創建值。

使用結構類型聲明變量,並初始化為零值

var nick Person

關鍵字 var 創建了類型為 Person 且名為 nick 的變量。nick 被稱作類型 Person 的一個實例(instance)或對象(object)。注意:當聲明變量時,這個變量對應的值總是會被初始化。這個值要么用指定的值初始化,要么用零值(即變量類型的默認值)做初始化。對數值類型來說,零值是 0;對字符串來說,零值是空字符串;對布爾類型,零值是 false。

通過上面的方式聲明結構體類型的變量,結構體里的每個字段都會用零值初始化。任何時候,創建一個變量並初始化為零值,習慣上會使用關鍵字 var。這種用法是為了更明確地表示一個變量被設置為零值。

使用結構體字面量聲明變量,並初始化為非零值
如果希望變量被初始化為某個非零值,可以通過結構體字面量和短變量聲明操作符(:=)來創建變量。下面的代碼展示了如何聲明一個 Persion 類型的變量,並使用某個非零值作為初始值。

// 聲明 Person 類型的變量,並初始化所有字段
nick := Person{
    Name: "nick",
    Age: 28,
    Email: "nickli@xxx.com",
}

在第一行中我們給出了一個變量名 nick,之后是短變量聲明操作符。這個操作符是冒號加一個等號 (:=)。一個短變量聲明操作符在一次操作中完成兩件事情:聲明一個變量,並初始化。短變量聲明操作符會使用右側給出的類型信息作為聲明變量的類型。

短變量聲明操作符(:=)
它的作用是聲明並且賦值一個變量,其好處是不需要寫 var 三個字母,另外不需要寫類型,Golang 語言會自動根據賦值的內容確定類型。

使用短變量聲明操作符也有一些限制,比如不能在函數外面使用,即不能用來聲明全局變量。另外短變量聲明操作符左邊至少得有一個變量是沒有定義過的。

字面量的兩種寫法
結構體字面量可以對結構體類型進行初始化,比如前面介紹過的示例:

nick := Person{
    Name: "nick",
    Age: 28,
    Email: "nickli@xxx.com",
}

這種形式在不同行聲明每個字段的名字以及對應的值。字段名與值用冒號分隔,每一行以逗號結尾。這種形式對字段的聲明順序沒有要求。
第二種形式沒有字段名,只聲明對應的值,如下面的代碼:

nick := Person{"nick", 28, "nickli@xxx.com"}

每個值也可以分別占一行,不過習慣上這種形式會寫在一行里,結尾不需要逗號。這種形式下,值的順序很重要,必須要和結構聲明中字段的順序一致。

new 函數

new 是一個用來分配內存的內置函數,但是與 C++ 不一樣的是,它並不初始化內存,只是將其置零。也就是說,new(T) 會為類型 T 分配被置零的內存,並且返回它的地址,一個類型為 *T 的值。在 Golang 的術語中,其返回一個指向新分配的類型為 T 的指針,這個指針指向的內容的值為零(zero value)。比如下面的代碼:

nick := new(Person)

這里的變量 nick 是一個指針:

fmt.Printf("%T", nick)

輸出的結果為:*main.Student
而通過下面方式聲明的變量:

var nick Person

類型則是:main.Student

使用 new 函數時,聲明變量和分配內存並不需要放在一起,可以先聲明一個變量,然后再通過 new 函數為之分配內存,比如下面的寫法:
var nick *Person
nick = new (Person)

new 函數的特點是只能把內存初始化為零值並返回其指針,如果要通過字面量初始化該內存就需要使用混合字面量語法(composite literal syntax)
&T{...}
比如下面的寫法:

nick := &Person{
    Name:  "nick",
    Age:   28,
    Email: "nickli@xxx.com",
}

此時 nick 的類型也是 *Person。因此我們可以得出下面的結論:
表達式 new(Type) 和 &Type{} 是等價的。

選擇器(selector)

在 Golang 中,訪問結構體成員需要使用點號操作符,點號操作符也被稱為選擇器(selector),使用時的格式為:

結構體.成員名

注意:這里的 "結構體" 是指結構體類型的變量或結構體指針類型的變量。無論變量是一個結構體類型還是一個結構體類型指針,都可以使用相同的選擇器服務來引用結構體的字段。

匿名字段和內嵌結構體

結構體可以包含一個或多個匿名(或者稱為內嵌)字段,即這些字段沒有顯式的名字。僅指明字段的類型,此時該類型就是字段的名字。匿名字段本身可以是一個結構體類型,即結構體可以包含內嵌的結構體。

匿名字段
匿名字段和面向對象編程中的繼承概念相似,可以被用來模擬類似繼承的行為。Golang 中的基礎就是通過內嵌或組合來實現的,所以說在 Golang 中組合比繼承更受歡迎。比如下面的例子:

type test struct {
    name string
    age int
    int // 匿名字段
}

內嵌結構體
結構體也是一種數據類型,所以它同樣可以作為匿名字段使用:

type Person struct {
    Name  string
    Age     int
    Email string
}
type Student struct {
    Person
    StudentID int
}

下面的代碼可以聲明並初始化 Student 類型的變量:

st := Student {
    Person:    Person{"jack", 22, "jack@xxx.com"},
    StudentID: 1000,
}

從這個示例可以看出,內層結構體被簡單地插入或者內嵌進外層結構體。這種簡單的 "繼承" 機制使得 Golang 很輕松就能實現從一個或一些類型中繼承部分或全部的實現。

總結

Golang 中沒有類的概念,因此在 Golang 中結構體有着更為重要的地位。所以學習並掌握結構體是入門 Golang 的關鍵步驟。希望本文能夠幫助大家理解 Golang 結構體的基本概念和用法。

參考:
Golang Struct types
《Go語言編程入門與實戰技巧》
結構體定義
go 語言的變量聲明並賦值運算符(:=)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM