结 构 struct
1、Go 中 的 struct 与 C 中 的 struct非常 相 似,并且 GO 没 有 class
2、使 用 type struct{} 定 义 结 构 , 名 称 遵 循 可 见 性 规 则
3、 支 持 指 向 自 身 的 指 针 类 型 成 员
4、支 持匿名 结 构, 可 用 作 成 员 或 定 义 成 员 变 量
5、匿 名 结 构 也 可 以 用 于 ma p 的 值
6、可 以 使 用 字 面值对结构进 行 初 始 化
7、允 许 直 接 通 过 指 针 来 读 写 结 构 成 员
8、相 同 类 型 的 成 员 可 进 行 直 接 拷 贝 賦 值
9、支 持 = = 与 ! = 比 较 运 算 符 , 但 不 寺 > 或 <
10、支 持 匿 名 字 段, 本 质 上 是 定 义 了 以 某 个 类 型 名 为 名 称 的 字 段
11、嵌 入 结 构 作 为 匿名 字 段 看 起 来 像 继 承 , 但 不 是 继 承
12、可 以 使 用 匿 名 字 段 指 针
首先来看看定义一个空的结构
type test struct {
}
当然他也是类型,只要是类型它就可以使用赋值
a:=test{}
fmt.Println(a)
运行结果也表示空的
{}
以下是定义一个结构以及属性、如何操作该结构的属性
type person struct {
name string
age int
}
func main() {
a:=person{}
a.age=15
a.name="Bruce"
fmt.Println(a)
}
运行结果
{Bruce 15}
也可以使用如下方法给赋值(这种方法也是Go语言中最常见的赋值手段)
a:=person{
name:"Bruce",
age:15}
这里值得注意的是,赋值的顺序和定义的顺序没有直接关系(后边说的匿名字段就不是这么一回事了)。
struct是值类型,进行的值传递,以下程序以及运行结果可以说明
func main() {
a:=person{
age:15,
name:"Bruce"}
fmt.Println(a)
test(a)
fmt.Println(a)
}
func test(person person){
person.age=100
person.name="Hello World"
fmt.Println(person)
}
运行结果
{Bruce 15}
{Hello World 100}
{Bruce 15}
从结果看出,确实是值传递,没有改变原始的值(因为没有进行内存地址传递)。
当然也可以使用指针来把struct的值传递改为内存地址的传递,这样就可以通过传递内存地址来修改原始的值。看如下程序
func main() {
a:=person{
age:15,
name:"Bruce"}
fmt.Println(a)
test(&a)
fmt.Println(a)
}
func test(person *person){
person.age=100
person.name="Hello World"
fmt.Println(*person)
}
运行结果
{Bruce 15}
{Hello World 100}
{Hello World 100}
这样确实改变了原来的值,因为使用的指针进行的内存地址的拷贝。
补充一下,&表示取地址的符号,星号是指针的表示,当星号用在本身是一个指针的身上,就表示去该指针指向的内存地址对应的值。就像上例中的 fmt.Println(*person)
建议在一个结构初始化的时候,可以进行取地址符号。如下:
func main() {
a:=&person{
age:15,
name:"Bruce"}
a.age=99
fmt.Println(*a)
test(a)
fmt.Println(*a)
}
func test(person *person){
person.age=100
person.name="Hello World"
fmt.Println(*person)
}
再一次修改a的值直接用点号(.)就可以了。这也是Go语言中比较特殊的地方,如上程序中a.age=99如果放在其他语言(如c语言)中,那得*a.age=99
匿名结构,类似于匿名函数,一个字,为了方便操作。匿名结构就是把定义属性和初始化的行为紧挨着实现,不需要结构的名字,因为它是被直接调用的。
如下程序:
func main() {
a:=struct{
name string
age int
}{
age:15,
name:"Bruce"}
a.age=99
fmt.Println(a)
}
运行结果
{Bruce 99}
方便操作结构是匿名结构的意义所在。
匿名结构嵌套的普通结构中的使用很常用,有必要好好研究一下,看如下程序:
(这里要值得注意的是匿名结构的初始化方法。)
type person struct{
age int
name string
contact struct{
phone,city string
}
}
func main() {
a:=person{
age:15,
name:"Bruce"}
a.contact.city="GuiLin"
a.contact.phone="123456789"
fmt.Println(a)
}
运行结果
{15 Bruce {123456789 GuiLin}}
来点Go语言中更加简约的地方,匿名字段。
type person struct{
int
string
}
func main() {
a:=person{
15,
"Bruce"}
fmt.Println(a)
}
运行结果
{15 Bruce}
这里值得注意的是使用匿名字段时,初始化字段的顺序要和定义匿名字段的顺序一致。当然匿名字段也可以跟一般的字段结合使用。如下情况
type person struct{
age int
string
}
func main() {
a:=person{
15,
"Bruce"}
fmt.Println(a)
}
结构是一种类型,这也就说明同种类型可以进行相互赋值。
type person struct{
age int
string
}
func main() {
a:=person{
15,
"Bruce"}
b:=a
fmt.Println(b)
}
这里省略了b的类型时person,Go语言底层机制自动识别,为了说明同种类型,可以使用如下定义b
var b person
b=a
Java或者C++中都有继承,在Go语言,使用结构的嵌套(也叫做组合)来实现类似的效果,值得注意的是类似并不等同。
type human struct{
sex int
age int
name string
}
type teacher struct{
human
level,number string
}
type student struct{
human
class string
score int
}
func main() {
teacherA:=teacher{
level:"middle",
number:"123456"}
teacherA.human.age=56
teacherA.human.sex=0
teacherA.human.name="Bruce"
fmt.Println(teacherA)
}
运行结果
{{0 56 Bruce} middle 123456}
组合型的结构的初始化除了上面那种还有可以有另外一种方法:
teacherA:=teacher{
level:"middle",
number:"123456",human:human{
sex:0,
age:56,
name:"Bruce"}}
fmt.Println(teacherA)
这里就涉及到Go语言的一个技术点,看程序human:human就知道了,因为human作为一个类型,嵌套进来那就是匿名字段,在Go语言中嵌入结构作为匿名字段被初始化时默认将结构名称human作为字段名称。
思考以下问题:
如 果 匿 名 字 段 和 外 层 结 构 有 同 名 字 段 , 应 该 如 何 进 行 操 作 ?
嵌套结构的同名字段的优先级没有外层结构同名字段的有限级别高,比如
type human struct{
sex int
age int
name string
}
type teacher struct{
name string
human
level,number string
}
type student struct{
name string
human
class string
score int
}
func main() {
teacherA:=teacher{
level:"middle",
number:"123456"}
teacherA.human.age=56
teacherA.human.sex=0
teacherA.name="Bruce"
fmt.Println(teacherA)
}
运行结果
{Bruce {0 56 } middle 123456}
这种情况不会报出语法错误,这时候要想匿名字段(这里指类型human)中的name赋值就必须加上匿名字段的类型human,如果没有与外层结构同名的话就不需要添加human这个类型名称。