Go通过类型别名(Alias Types)和结构体的形式支持用户自定义类型。结构体是复合类型(Composite Types)。组成结构体类型的数据称为字段(FIElds)。每个字段都有一个类型和名称;单个结构体中名称必须是唯一的。
type IDentifIEr struct { fIEld1 type1 fIEld2 type2 // ...}
另外,定义简单的结构体可以使用:
type T struct {a, b int}
结构体中的字段都有名称,但是如果某字段不会被使用,可以命名为_
。
结构体的字段可以是任何类型,甚至是结构体本身,也可以是函数或者接口。
分配使用new
为结构体分配内存,这将返回已分配内存的指针。
var t *T = new(T)
这条语句惯用写成:
t := new(T)
t
是指向T
的指针。new
完成后,结构体内字段的值是其所属类型的零值。
如果声明采用:
var t T
这样也会给t
分配内存,并零值化,但此时t
的类型是T
。
以上两种声明方式中,t
通常被称为类型T
的一个实例(Instance)或对象(Object)。
示例:
package main import "fmt"type struct1 struct { i1 int f1 float32 str string}func main() { ms := new(struct1) ms.i1 = 10 ms.f1 = 15.5 ms.str = "4thrun" fmt.Printf("The int is: %d\n", ms.i1) fmt.Printf("The float is: %f\n", ms.f1) fmt.Printf("The string is: %s\n", ms.str) fmt.Println(ms)}
运行结果:
The int is: 10The float is: 15.500000The string is: 4thrun&{10 15.5 4thrun}
结构体递归结构体类型可以通过引用自身来定义。在定义二叉链表、二叉树等结构时,通常会用到这种定义方式(用结构体表示结点)。在结点信息中,需要包含临近结点的链接(地址)。如下所示:
// 链表结构体type Node struct { data float64 su *Node // 存放下一地址}
// 双向链表type Node struct { pr *Node data float64 su *Node }
// 二叉树type Tree struct { le *Tree data float64 ri *Tree}
结构体转换当为结构体定义了Alias类型时,此结构体和它的Alias类型具有相同的底层类型。它们可以使用如下代码进行相互转化,非法赋值或转换会引起编译错误:
package main import "fmt"type number struct { f float32}type nr number // Alias type func main() { a := number{5.0} b := nr{5.0} // var i float32 = b // compile-error: cannot use b (type nr) as type float32 in assignment // var i = float32(b) // compile-error: cannot convert b (type nr) to type float32 // var c number = b // compile-error: cannot use b (type nr) as type number in assignment // needs a conversion: var c = number(b) fmt.Println(a, b, c)}
匿名字段与struct嵌套package mainimport ( "fmt")type inner struct { in1 int in2 int}type outer struct { ou1 int ou2 int int inner}func main() { o := new(outer) o.ou1 = 1 o.ou2 = 2 o.int = 3 o.in1 = 4 o.in2 = 5 fmt.Println(o.ou1) fmt.Println(o.ou2) fmt.Println(o.int) fmt.Println(o.in1) fmt.Println(o.in2) }
输出为:
12345
工厂模式Go不支持传统面向对象语言的构造子方法,但是可以实现构造子工厂。通常会为一个类型定义工厂模式,工厂名称以new
或者New
开头,便于识别。如以下file
结构体类型:
type file struct { fd int // 文件描述符 name string // 文件名}
对这个结构体定义工厂方法,返回指向结构体实例的指针:
func Newfile(fd int, name string) *file { if fd < 0 { return nil } return &file{fd, name}}
如果file
是结构体类型,那么new(file)
和&file{}
两个表达式是等价的。
通过禁止使用new
函数,强制使用工厂方法,可以让类型变成私有,达到面向对象语言中的相同效果。
type matrix struct { // ... }func NewMatrix(params) *matrix { m := new(matrix) // 初始化m return m}
在其它包使用工厂方法:
package main import "matrix"// ...wrong := new(matrix.matrix) // 编译失败,matrix是私有的right := matrix.NewMatrix(...) // 实例化matrix的唯一方法
结构体传值/传指Go函数给参数传递值时是以复制的方式进行的。复制传值时,如果函数的参数是一个结构体对象,将复制整个数据结构的副本传递给函数,这会导致两个问题:
函数内部无法修改传递给函数的原始数据结构,只能修改副本;如果原始数据结构很大,传值代价是不能接受的。原则上都应该传递指针,而不是传递副本。
结构体方法结构体中可以包含属性,同样结构体可以有方法。
方法实际上就是函数,结构体方法即是具有特殊接收器类型的函数。接收器用于在函数内部进行访问。
func (t Type) methodname (parameter List) { // ...}
许多编程面向对象的编程语言,都会有this
或者self
隐式地指向当前实例,但是Go中没有这个概念,定义方法时都会给定变量名。
对于方法而言,使用引用和使用值区别不大,编译时也会自行优化。
总结以上是内存溢出为你收集整理的Go入门(11)——结构体全部内容,希望文章能够帮你解决Go入门(11)——结构体所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)