[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.结构体和结构体变量(实例)的区别和联系
- 结构体是自定义的数据类型,代表一类事物.
- 结构体变量(实例)是具体的,实际的,代表一个具体变量