golang语言 结构体
type abc struct { x, y int u float32 "标注" _ float32 // padding A *[]int F func() } var a abc // 声明变量,会开辟一段内存 t := &a.x // 对成员取地址 . 的优先级比 * 和 & 高 // 用map模拟set时,struct{}大小为0,可节约空间,但语法复杂,通常不这么做 map[string]struct{} map["a"] = struct{}{}
S类型的结构体不能直接或间接的包含S类型的成员,可以包含*S类型的指针成员
结构体字面值
- 键必须为字面类型中声明的字段名。
- 不包含任何键的元素列表必须按字段的声明顺序列出每个结构字段的元素。
- 若其中任何一个元素有键,那么每个元素都必须有键。
- 包含键的元素列表无需每个结构字段都有元素。被忽略的字段会获得零值
- 字面可忽略元素列表;这样的字面对其类型求值为零值。
- 为属于不同包的结构的未导出字段指定一个元素会产生一个错误。
pp := &Point{1, 2}
它和下面的语句是等价的
pp := new(Point)
*pp = Point{1, 2}
下面多引入了一个变量 t:
t := Point{1, 2}
pp := &t
x := new(Point).x
y := (&Point{}).y
下面两种方式是等价的,
其中Wheel包含匿名字段Circle,Circle包含匿名字段Point,Point包含字段X、Y
w = Wheel{Circle{Point{8, 8}, 5}, 20} // 要按顺序
w = Wheel{
Circle: Circle{
Point: Point{X: 8, Y: 8},
Radius: 5,
},
Spokes: 20, // 逗号和 } 不在同一行,逗号不能省略,在同一行可以省略
}
匿名字段 / 嵌入式字段
有类型,但没有显式的字段名声明,类型名的行为类似于字段名。类型可以是T或者*T,如果类型是*T,那么T本身不能是接口类型,也不能是指针,即不能是**Point。匿名成员也有可见性约束,类型名以大写开头则是导出的。
struct { // 带类型为T1,*T2,P.T3和*P.T4的4个匿名字段的结构 T1 // 字段名为T1 *T2 // 字段名为T2 T2可能是 type T2 *Point P.T3 // 字段名为T3 *P.T4 // 字段名为T4 x, y int // 字段名为x和y }
type T struct { *log.Logger }
访问 T 类型的变量 t 的 *log.Logger,可以忽略包限定名,直接写作 t.Logger。
内嵌一个接口,可看作实现了这个接口
type P struct{}
func (p P) add() int{ return 1 }
type S struct{ I }
type I interface { add() int }
s := S{ } // {<nil>}
var i I = s // ok s实现了接口I
i.add() // panic 即使s没有实现add方法
s = S{ P{} } // { {} }
i = s
i.add() // 1
匿名结构体类型作为字段
var aBook = struct { author struct { // 此字段的类型为一个匿名结构体类型 firstName, lastName string gender bool } title string pages int }{ author: struct { firstName, lastName string gender bool }{ firstName: "Mark", lastName: "Twain", }, // 此组合字面量中的类型为一个匿名结构体类型 title: "The Million Pound Note", pages: 96, }
选择器
x.f,若f是x的属性或方法,f的深度为0,若f是x的匿名字段的字段或方法,f的深度为1,以此类推
x.f是x的深度最浅的名为f的字段或方法,若有多个相同深度的重名字段名,这些字段如果在结构体定义之外不会被访问,则是合法的,否则x.f非法
在结构体 x 中,若 x.f 为合法的选择者,那么匿名字段的字段或方法 f 即为"已提升(promoted)"的,
已提升字段不能用作该结构体字面量中的字段名,其他行为和结构体中的普通字段一样
选择者会自动解引用指向结构的指针。 若 x 为指向结构的指针,x.y 即为 (*x).y 的缩写; 若字段 y 亦为指向结构的指针,x.y.z 即为 (*(*x).y).z 的缩写, 以此类推。 若 x 包含类型为 *A 的匿名字段,且 A 亦为结构类型, x.f 即为 (*x.A).f 的缩写。
type T0 struct { x int } func (*T0) M0() type T1 struct { y int } func (T1) M1() type T2 struct { z int T1 *T0 } func (*T2) M2() type Q *T2 var t T2 // with t.T0 != nil var p *T2 // with p != nil and (*p).T0 != nil var q Q = p 正确: t.z // t.z t.y // t.T1.y t.x // (*t.T0).x p.z // (*p).z p.y // (*p).T1.y p.x // (*(*p).T0).x q.x // (*(*q).T0).x (*q).x is a valid field selector p.M0() // ((*p).T0).M0() M0 expects *T0 receiver p.M1() // ((*p).T1).M1() M1 expects T1 receiver p.M2() // p.M2() M2 expects *T2 receiver t.M2() // (&t).M2() M2 expects *T2 receiver, see section on Calls 错误: q.M0() // (*q).M0 is valid but not a field selector
指针值和选择器
基本规则:
一般说来,定义的指针类型的值不能使用在选择器语法形式中。
语法糖:
如果值x的类型为一个一级定义指针类型,并且(*x).f是一个合法的选择器,则x.f也是合法的。
多级指针均不能出现在选择器语法形式中。
上述语法糖的例外:
上述语法糖只对f为字段的情况才有效,对于f为方法的情况无效。
例子:
package main type T struct { x int } func (T) y() { } type P *T type PP **T // 一个多级指针类型。 疑问 PP如果是*P类型呢? func main() { var t T var p P = &t var pt = &t // pt的类型为*T var ppt = &pt // ppt的类型为**T var pp PP = ppt _ = pp _ = (*p).x // 合法 _ = p.x // 合法(因为x为一个字段) _ = (*p).y // 合法 // _ = p.y // 不合法(因为y为一个方法) // 下面的选择器均不合法。 /* _ = ppt.x _ = ppt.y _ = pp.x _ = pp.y */ }