结构体的内存布局
Go 语言中,结构体和它所包含的数据在内存中是以连续块的形式存在的,即使结构体中嵌套有其他的结构体,这在性能上带来了很大的优势。
递归结构体
递归结构体类型可以通过引用自身指针来定义。这在定义链表或二叉树的节点时特别有用,此时节点包含指向临近节点的链接。例如:
type Element struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Element
// The list to which this element belongs.
list *List
// The value stored with this element.
Value interface{}
}
可见性
通过参考应用可见性规则,如果结构体名不能导出,可使用 new 函数使用工厂方法的方法达到同样的目的。例如:
type bitmap struct {
Size int
data []byte
}
func NewBitmap(size int) *bitmap {
div, mod := size/8, size%8
if mod > 0 {
div++
}
return &bitmap{size, make([]byte, div)}
}
在包外,只有通过NewBitmap函数才可以初始bitmap结构体。同理,在bitmap结构体中,由于其字段data是小写字母开头即并未导出,bitmap结构体的变量不能直接通过选择器读取data字段的数据。
带标签的结构体
结构体中的字段除了有名字和类型外,还可以有一个可选的标签(tag)。它是一个附属于字段的字符串,可以是文档或其他的重要标记。标签的内容不可以在一般的编程中使用,只有 reflect 包能获取它。
reflect包可以在运行时反射得到类型、属性和方法。如变量是结构体类型,可以通过 Field() 方法来索引结构体的字段,然后就可以得到Tag 属性。例如:
package main
import (
"fmt"
"reflect"
)
type Student struct {
name string "学生名字" // 结构体标签
Age int "学生年龄" // 结构体标签
Room int `json:"Roomid"` // 结构体标签
}
func main() {
st := Student{"Titan", 14, 102}
fmt.Println(reflect.TypeOf(st).Field(0).Tag)
fmt.Println(reflect.TypeOf(st).Field(1).Tag)
fmt.Println(reflect.TypeOf(st).Field(2).Tag)
}
程序输出:
学生名字
学生年龄
json:"Roomid"
从上面代码中可以看到,通过reflect我们很容易得到结构体字段的标签。