[toc]
>結構體(struct)是Go支持面向對象編程特性的基礎
# 1.聲明結構體
`結構體的定義只是一種內存布局的描述,只有當結構體實例化時,才會真正地分配內存。因此必須在定義結構體并實例化后才能使用結構體的字段。
實例化就是根據結構體定義的格式創建一份與格式一致的內存區域,結構體實例與實例間的內存是完全獨立的。`
## 1.1 基本語法
```
type 結構體名稱 struct {
field type
field type
}
```
> <font color=red>字段直接沒有逗號(,)
- field: 從概念或叫法上稱: 結構體字段、屬性
- type:一般是基本數據類型、數組,也可是引用類型
- 同類型變量可以寫在一行
```
type Human struct {
name string
age,sex,height int
}
```
# 2.結構體實例化
## 2.1 基本實例化形式(使用關鍵字: var)
```
type Girl struct {
Name string
}
func UseStruct() {
//使用關鍵字var
var g Girl
g.Name = "小芳"
fmt.Println(g) // 輸出: {小芳 0 0 0}
}
```
> 上述變量 g 為結構體的實例
## 2.2 創建指針類型的結構體(使用關鍵字: new)
```
type Girl struct {
Name string
}
func UseStruct() {
// 使用new
g1 := new(Girl)
g1.Name = "小芳"
fmt.Println(g1) // 輸出: &{小芳 0 0 0}
}
```
> Girl類型被實例化后保存到g1變量中,g1的類型為*Girl,屬于指針
> <font color=red>經過new實例化的結構體實例在成員賦值上與基本實例化的寫法一</front>
## 2.3 取結構的地址實例化(使用: &)
**在Go語言中,對結構體進行“&”取地址操作時,視為對該類型進行一次new的實例化操作**
```
type Girl struct {
Name string
}
func UseStruct() {
// 使用&
g2 := &Girl{}
g2.Name = "小芳"
fmt.Println(g2) // 輸出: &{小芳 0 0 0}
}
```
# 3. 初始化結構體字段用例
## 3.1 遞歸填充
```
type Family struct {
name string
child *Family
}
func UseStruct() {
p := Family{
name: "爺爺",
child: &Family{
name: "爸爸",
child: &Family{
name: "我",
},
},
}
fmt.Println(p.name) // 爺爺
fmt.Println(p.child.name) //爸爸
fmt.Println(p.child.child.name) // 我
}
```
## 3.2 多值初始化
```
type Girl struct {
Name string
Age int
Height int
Weight float32
}
func CreateStruct() {
// 順序填充
g1 := Girl{"小花", 18, 170, 55.4}
fmt.Println(g1) // {小花 18 170 55.4}
// 指定字段填充
g := Girl{Name:"小雨", Age:19}
fmt.Println(g) // {小雨 19 0 0}
}
```
## 3.3 匿名結構體初始化
`結構體可以包含一個或多個 匿名(或內嵌)字段,即這些字段沒有顯式的名字,只有字段的類型是必須的,此時類型就是字段的名字。匿名字段本身可以是一個結構體類型,即 結構體可以包含內嵌結構體`
```
package main
import "fmt"
type Human struct {
Name string
Age, Sex int
}
type Girl struct {
Human //匿名字段
Like string
}
func main() {
p := Girl{Human{Name: "小芳", Age: 18}, "唱歌"}
fmt.Println(p)
}
```
# 4. 帶標簽的結構體
`以文檔或其他的重要標記。標簽的內容不可以在一般的編程中使用,只有reflect(反射) 能獲取它。`
## 4.1 使用示例
```
package main
import (
"reflect"
"fmt"
)
type SchoolTag struct {
Name string `school name`
Address string `school address`
Tel string `school tel`
}
func main() {
schTag := SchoolTag{"名牌大學", "天朝1號", "010-1234560"}
count := reflect.ValueOf(schTag).NumField()
for i := 0; i < count; i++ {
fmt.Printf("%v\n",reflect.TypeOf(schTag).Field(i).Tag)
}
}
```
# 5. 結構體比較
`如果結構體的所有成員變量都可以比較,那么這個結構體就是可比較的。兩個結構體的比較用 == 或者 != 其中== 按照順序比較兩個結構體變量的成員`
```
package main
import "fmt"
type Point struct {
X, Y int
}
func main() {
p1 := Point{1,2}
p2 := Point{1,3}
//表達式1
fmt.Println(p1.X == p2.X && p1.Y == p2.Y) //false
//表達式2
fmt.Println(p1 == p2) //false
}
```
> 表達式1和表達式2 是等價的
**可比較的結構體都可以作為map的鍵類型**
示例(統計某個鏈接的點擊次數):
```
package main
import "fmt"
type UrlCount struct {
Controller, Action string
}
func main() {
m := make(map[UrlCount]int)
for i := 0; i < 5; i++ {
m[UrlCount{"home", "index"}]++
}
fmt.Println(m) // map[{home index}:5]
}
```
# 6 結構體特性
- 在創建一個結構體變量后,如果沒有給字段賦值,都對應一個零值(默認值)
- 結構體是值類型,默認為值拷貝
- 字段名必須唯一
- 結構體的所有字段在內存中是連續的
- 結構體是用戶單獨定義的類型,和其它類型進行轉換時需要有完全相同的字段(名字、個數和類型)
```
package main
import "fmt"
type A struct {
X, Y int
}
type B struct {
X, Y int
}
func main() {
var a A
var b B
a = A(b) // 類型轉換時,需要有完全相同的字段(名字、個數和類型)
fmt.Printf("%T \n",a)
fmt.Printf("%T \n",b)
}
```
- 結構體進行 type 重新定義(相當于取別名),Go 認為是新的數據類型,但是相互間可以強轉
```
package main
type Human struct {
Name string
Age int
}
type Boy Human
func main() {
var h Human
var b Boy
//這么寫錯誤,原因:Go認為Boy是新的數據類型
// h == b
//可以相互間強轉
b = Boy(h)
h = Human(b)
}
```
# 7.結構體和結構體變量(實例)的區別和聯系
- 結構體是自定義的數據類型,代表一類事物.
- 結構體變量(實例)是具體的,實際的,代表一個具體變量