结构体

Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

与C++,Java有所不同,Go没有class的概念,只有结构体struct,其可以拥有属性和方法,我们可以利用这一特性来实现面向对象编程。

定义结构体

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:

type Student struct {
    member definition
    member definition
    ...
}

匿名字段

对于匿名字段,必须将匿名字段指定为类型名称或指向非接口类型名称 *T的指针,并且T可能不是指针类型。

struct {
    T1          // 字段名 T1
    *T2       // 字段名 T2
    P.T3      // 字段名 T3
    *P.T4     // f字段名T4
    x, y int    // 字段名 x 和 y
}

使用new函数创建结构体

和其他语言一样,Go也可以使用关键字new来实例化一个结构体,并分配内存:

type Car struct {
    name string
    price int
}

func main()  {
    my_car := new(Car)  // new函数声明并初始化一个实例

    var your_car Car    // 这样声明的变量是类型T

    // 在这两种方式中,t 通常被称做类型 T 的一个实例(instance)或对象(object)。
}

初始化结构体

intr := Car{"大众", 200000} // 按照字段顺序初始化
intr := Car{name: "长城", price: 190000} // 显示化声明
intr := Car{name: "奥迪"} // 也可以只声明部分字段

命名冲突

当结构体的字段一样时,外层名字覆盖内层名称;当同级冲突时,会报错,不过可以采用逐级选择使用的方式来避免这个错误。

type A struct {
    name string
}
type B struct {
    name string
}

type C struct {
    A
    B
}

var c C

上面如果直接使用c.name时会报错的,但是可以c.A.name的形式就ok了。

访问结构体成员

如果要访问结构体成员,需要使用点号 . 操作符,格式为:

package main

import "fmt"

type Book struct {
    title string
    author string
    book_id int
}

func main()  {
    var book1 Books

    book1.title = "Go学习笔记"
    book1.author = "zuojt"
    book1.book_id = 12938139439

    fmt.Println(book1.title)
}

结构体特性

内存布局

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)}
}

标签

Go的字段还有一个标签(tag),用于编辑文档和内容,但是只有reflect包能获取它。 reflect包可以在运行时反射得到类型、属性和方法。如变量是结构体类型,可以通过 Field() 方法来索引结构体的字段,然后就可以得到Tag 属性。

package main

import (
    "fmt"
    "reflect"
)
type Student struct {
    name string "学生名字"
    age  int    "学生年龄"
}

func main() {
    stu := Student{"tom", 13}

    fmt.Println(reflect.TypeOf(stu).Field(0).Tag)
}

结构体作为函数参数

你可以像其他数据类型一样将结构体类型作为参数传递给函数。

...

func printBook(book Book)  {
    fmt.Println(book1.title)
}

结构体指针

  • 你可以定义指向结构体的指针类似于其他指针变量,格式如下:
var struct_pointer *Book
  • 以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前:
struct_pointer = &Book

结构体的继承

和其他语言的类一样,Go的结构体也可以继承属性和方法。

package main

import "fmt"

type Human struct {
    name string
    age int
}

type Man struct {
    Human
    sex int
    score int
}

func main()  {
    var student Man
    student.score = 90
    student.age = 26

    fmt.Println(student.score, student.age)
}

results matching ""

    No results matching ""