结构体基础
# 1. 结构体基础
# 面向对象介绍
(1)Golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说Golang支持面向对象编程特性是比较准确的。 (2)Golang没有类(class),Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解Gelang是基于struct来实现OOP特性的。 (3)Golang面向对象编程非常简洁,去掉了传统OOP语言的方法重载、构造函数和析构函数、隐藏的this指针等等 (4)Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样,比如继承:Golang没有extends 关键字,继承是通过匿名字段来实现。
# 结构体定义
Go 语言通过用自定义的方式形成新的类型,结构体是类型中带有成员的复合类型。Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性。
Go 语言中的类型可以被实例化,使用 new 或 & 构造的类型实例的类型是类型的指针。
结构体成员是由一系列的成员变量构成,这些成员变量也被称为“字段”。字段有以下特性:
字段拥有自己的类型和值。
字段名必须唯一。
字段的类型也可以是结构体,甚至是字段所在结构体的类型。
使用关键字
type
可以将各种基本类型定义为自定义类型,基本类型包括整型、字符串、布尔等。结构体是一种复合的基本类型,通过 type 定义为自定义类型后,使结构体更便于使用。
结构体的定义格式如下:
type 类型名 struct {
字段1 字段1类型
字段2 字段2类型
…
}
2
3
4
5
对各个部分的说明:
类型名:标识自定义结构体的名称,在同一个包内不能重复。
struct{}:表示结构体类型,
type 类型名 struct{}
可以理解为将 struct{} 结构体定义为类型名的类型。字段1、字段2……:表示结构体字段名,结构体中的字段名必须唯一。
字段1类型、字段2类型……:表示结构体各个字段的类型。
使用结构体可以表示一个包含 X 和 Y 整型分量的点结构,代码如下:
type Point struct {
X int
Y int
}
2
3
4
同类型的变量也可以写在一行,颜色的红、绿、蓝 3 个分量可以使用 byte 类型表示,定义的颜色结构体如下:
type Color struct {
R, G, B byte
}
2
3
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存,我们将在下一节《实例化结构体 (opens new window)》中详细为大家介绍。
package main
// 方式一
type point struct {
X int
Y int
}
// 方式二
type color struct {
R, G, B int
}
2
3
4
5
6
7
8
9
10
11
12
# 实例化结构体——为结构体分配内存并初始化
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存,因此必须在定义结构体并实例化后才能使用结构体的字段。
实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的内存是完全独立的。
# 基本的实例化形式
结构体本身是一种类型,可以像整型、字符串等类型一样,以 var 的方式声明结构体即可完成实例化。
基本实例化格式如下:
var ins T
其中,T 为结构体类型,ins 为结构体的实例。
var p point
fmt.Println(p) // 未赋值时都有默认值
p.X = 10
p.Y = 20
fmt.Printf("点(%d,%d)", p.X, p.Y)
{0 0}
点(10,20)
2
3
4
5
6
7
8
使用.
来访问结构体的成员变量,如p.X
和p.Y
等,结构体成员变量的赋值方法与普通变量一致。
# 实例化定义默认值
// 方式二: 创建带有默认值
var p1 point = point{10, 20}
fmt.Printf("点(%d,%d)\n", p1.X, p1.Y)
点(10,20)
2
3
4
5
# 创建指针类型的结构体(使用new关键字)
使用 new 关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。
使用 new 的格式如下:
ins := new(T)
其中:
T 为类型,可以是结构体、整型、字符串等。
ins:T 类型被实例化后保存到 ins 变量中,ins 的类型为 T,属于指针。
Go语言让我们可以像访问普通结构体一样使用 . 来访问结构体指针的成员。
// 方式三: 使用new关键字
p3 := new(point)
p3.X, p3.Y = 11, 12
fmt.Printf("点(%d,%d)\n", p3.X, p3.Y)
点(10,20)
2
3
4
5
6
经过 new 实例化的结构体实例在成员赋值上与基本实例化的写法一致。
Go语言和 C/C++ (opens new window)
在 C/C++ 语言中,使用 new 实例化类型后,访问其成员变量时必须使用 -> 操作符。
在Go语言中,访问结构体指针的成员变量时可以继续使用 . 这是因为Go语言为了方便开发者访问结构体指针的成员变量,使用了语法糖(Syntactic sugar)技术,将 ins.Name 形式转换为 (ins).Name。
# 取结构体的地址实例化
在Go语言中,对结构体进行&
取地址操作时,视为对该类型进行一次 new 的实例化操作,取地址格式如下:
ins := &T{}
其中:
T 表示结构体类型。
ins 为结构体的实例,类型为 T,是指针类型。
// 方法四: 通过指针
p4 := &point{13, 14}
fmt.Printf("点(%d,%d)\n", p4.X, p4.Y)
点(13,14)
2
3
4
5
package main
import "fmt"
type point struct {
X int
Y int
}
func main() {
// 方式一: 普通方式
var p point
fmt.Println(p) // 未赋值时都有默认值
p.X = 10
p.Y = 20
fmt.Printf("点(%d,%d)\n", p.X, p.Y)
// 方式二: 创建带有默认值
var p1 point = point{10, 20}
fmt.Printf("点(%d,%d)\n", p1.X, p1.Y)
// 方式三: 使用new关键字
p3 := new(point)
p3.X, p3.Y = 11, 12
fmt.Printf("点(%d,%d)\n", p3.X, p3.Y)
// 方法四: 通过指针
p4 := &point{13, 14}
fmt.Printf("点(%d,%d)\n", p4.X, p4.Y)
}
/*
{0 0}
点(10,20)
点(10,20)
点(11,12)
点(13,14)
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 结构体之间的转换
package main
import "fmt"
type Student struct {
Age int
}
type Person struct {
Age int
}
// Stu 别名
type Stu Student
func main() {
// 方式一: 结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
var s Student = Student{10}
var p Person = Person{10}
s = Student(p)
fmt.Println(s)
fmt.Println(p)
// 方式二: 结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转
var s1 Student = Student{19}
var s2 Stu = Stu{19}
s1 = Student(s2)
fmt.Println(s1)
fmt.Println(s2)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 初始化结构体的成员变量(指定默认值)
更多递归方式直接看链接上面的示例
Go语言初始化结构体的成员变量 (opens new window)
package main
import "fmt"
type person struct {
name string
age int
}
func main() {
// 方式: 按照顺序传值
v1 := person{"小明", 12}
fmt.Println(v1)
// 方式二: 指定传参
v2 := person{name: "小虎", age: 18}
fmt.Println(v2)
// 方式三: 返回结构体的指针类型
var v3 *person = &person{"唐小龙", 20}
fmt.Println(v3)
fmt.Println(*v3)
}
/*
{小明 12}
{小虎 18}
&{唐小龙 20}
{唐小龙 20}
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 挎包创建结构体实例
【1】创建不同的包:
【2】student.go:
【3】main.go:
发现:如果结构体首字母大写的话,在其它包下可以访问 但是:如果结构体的首字母小写?
解决:结构体首字母小写,跨包访问没问题:---》工厂模式
# 示例
package main
import "fmt"
// 定义老师结构体,将老师中的各个属性 统一放入结构体中管理:
type Teacher struct {
//变量名字大写外界可以访问这个属性
Name string
Age int
School string
}
func main() {
//创建老师结构体的实例、对象、变量:
var t1 Teacher // var a int
fmt.Println(t1) //在未赋值时默认值:{ 0 }
t1.Name = "马士兵"
t1.Age = 45
t1.School = "清华大学"
fmt.Println(t1)
fmt.Println(t1.Age + 10)
}
/*
{ 0 }
{马士兵 45 清华大学}
55
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27