Go的第10课——类型定义和结构体
类型定义
定义语法跟之前的函数类型一样,使用 type 关键字
这里我们是给 oldtype 定义了一个别名,相当于是 typedef
只是 go
里面相对于 C 是反着写的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package main
import "fmt"
type pid_t int
func main() { var pid pid_t pid = 1 if pid == 1 { fmt.Println("SYSTEM PROCESS") } else { fmt.Println("COMMON PROCESS") } }
|
结构体
基本介绍
go语言没有面向对象的概念了,但是可以使用结构体来实现,面向对象编程的一些特性,例如:继承、组合等特性。
在这里我们结合上面的定义去创建一个结构体类型。
1 2 3 4 5 6
| type struct_identifier struct{ member1 type1 member2 type2 ... membern typen }
|
demo
随便定义一个结构体玩玩
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package main
import "fmt"
type Person struct { age int name string height int }
func main() { var person Person person.name = "xia0ji233" person.age = 22 person.height = 185 fmt.Println(person) }
|
结构体成员在没有赋值之前都是默认值。
结构体初始化
第一个就是按照上面的 demo 给成员一个一个赋值。
第二个就是使用键值对的方式初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import ( "fmt" )
type Person struct { age int name string height int }
func main() { var person = Person{age: 18, name: "xia0ji233", height: 185} fmt.Println(person) }
|
还有一个应该是可以使用成员方法的,这个不叫构造方法,因为它没有面向对象的概念,所以说也只能是定义一个成员方法去给它初始化。
结构体指针
基本用法
就和普通类型的指针差不多的用法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package main
import "fmt"
type Person struct { age int name string height int }
func main() { var person = Person{age: 18, name: "xia0ji233", height: 185} var p *Person p = &person fmt.Printf("name:%v\n", (*p).name) fmt.Printf("age:%v\n", (*p).age) fmt.Printf("height:%v\n", (*p).height) }
|
new关键字创建结构体指针
我们还可以通过使用 new
关键字对结构体进行实例化,得到的是结构体的地址,也就是指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package main
import "fmt"
type Person struct { age int name string height int }
func main() { var p *Person p = new(Person) (*p).name = "xia0ji233" (*p).age = 18 (*p).height = 185 fmt.Printf("name:%v\n", (*p).name) fmt.Printf("age:%v\n", (*p).age) fmt.Printf("height:%v\n", (*p).height) }
|
这里还发现一点,就是 C 语言对于指针访问结构体变量是只能 (*ptr).member
去访问,或者是 ptr->member
这样子的,而 go 就爽了,指针和结构体类型都是可以直接用 .
来访问成员变量的,可能是因为对指针做了限制,导致 ptr.member
写法就没什么歧义,因此可以。
方法
基本用法
go 语言没有面向对象的特性,也没有类对象的概念。但是,可以使用结构体来模拟这些特性,我们都知道面向对象里面有类方法等概念。我们也可以声明一些方法,属于某个结构体。
Go中的方法,是一种特殊的函数定义于struct之上(与struct关联、绑定),被称为struct的接受者(receiver)。
通俗的讲,方法就是有接收者的函数。
语法格式如下:
1 2 3
| type mytype struct{} func (recv mytype) my_method(para) return_type {} func (recv *mytype) my_method(para) return_type {}
|
和一般的函数对比起来,多了一个接受类型(recv) 其实这也可以看成一个参数,只不过因为是结构体主动调用的,所以这个参数可以不用我们主动传。
如果我们需要修改成员那就使用指针传参,如果不需要那么可以直接结构体传参,下面实现一个 init
的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package main
import "fmt"
type Person struct { age int name string height int }
func (recv *Person) init(age int, name string, height int) { *recv = Person{ age: age, name: name, height: height, } }
func main() { var p *Person p = new(Person) p.init(18, "xiaoji233", 185) fmt.Printf("name:%v\n", (*p).name) fmt.Printf("age:%v\n", (*p).age) fmt.Printf("height:%v\n", (*p).height) }
|
注意事项
- 方法的receiver type并非一定要是struct类型,type定义的类型别名、slice,map,channel,func类型等都可以。
- struct结合它的方法就等价于面向对象中的类。只不过struct可以和它的方法分开,并非一定要属于同一个文件,但必须属于同一个包。
- 方法有两种接收类型:(T Type)和(T*Type),它们之间有区别。
- 方法就是函数,所以Go中没有方法重载(overload)的说法,也就是说同一个类型中的所有方法名必须都唯一。
- 如果receiver是一个指针类型,则会自动解除引用。
- 方法和type是分开的,意味着实例的行为(behavior)和数据存储(field)是分开的,但是它们通过receiver建立起关联关系。
经过自己实验呢,发现基础数据类型确实不能,但是自己往基础类型创建一个别名就可以使用,弹幕里面有一句话叫 “扩展方法”,姑且认为是这样吧,方法和函数差不多,也不支持重载。
这边再写一个小例子巩固一下,弥补一下之前 int 转 string 不能强转的缺陷,为它写一个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import "fmt"
type pid_t int
func (recv pid_t) tostring() string { return fmt.Sprintf("%d", recv) }
func main() { var s pid_t s = 2147483647 var q = "value:" + s.tostring() fmt.Println(q) }
|