结构体

结构体是一个由用户定义的复合类型,它由一系列属性组成,每个属性都有自己的类型和值。Go语言中数组可以存储同一类型的数据,但在结构体中用户可以为不同项定义不同(或相同)的数据类型。结构体是值类型,因此可以通过new()函数来创建。

定义结构体

结构体的定义需要使用typestruct语句。struct语句定义一个新的数据类型,type语句用来设定结构体的名称。

  1. type identifier struct {
  2. member1 type1
  3. member2 type2
  4. }
  5. //当然如果有属性的类型时相同的你也可以下面的方式简写
  6. type identifier struct {
  7. //member1和member2就是同一种数据类型
  8. member1, member2 type1
  9. member3 type2
  10. }

结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正的分配内存。因此必须在定义结构体并实例化以后才能使用结构体的字段。

声明一个变量

当完成了对结构体的定义之后,就可以使用如下的方式来声明一个结构体变量。

  1. var_name := struct_identifier {value1,value2,value3...}

  2. var_name := struct_identifier {key1:value1,key2:value2,key3:value3...}

访问结构体的成员

  1. 结构体.成员名

结构体的零值

当定义一个机构体以后,并且没有显示地使用任何值初始化时,结构体的每一个字段都默认分配为零值。

下面我们定义一一个学生的结构体,其包含的属性为姓名和年龄。初始化s1并赋值,初始化s2但不赋值。

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. name string
  5. age int
  6. }
  7. func main() {
  8. s1 := Student{"zs", 12}
  9. var s2 Student
  10. fmt.Println(s1)
  11. fmt.Printf("name=%s,age=%d", s2.name, s2.age)
  12. }

输出结果:

  1. {zs 12}
  2. name=,age=0
  3. Program exited.

指向结构体的指针

Go语言可以使用&对结构体进行取地址操作,来创建指向结构体的指针。

  1. var1 := &struct_type{}

还可以使用new()函数来创建结构体指针,以结构体类型为参数,返回一个指向结构体的指针,并且为其指向的结构体变量分配零值。

  1. var1 := new(struct_type)

示例:

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. name string
  5. age int
  6. }
  7. func main() {
  8. //s1 := &Student{"zs", 12} 你也可以使用这种方式创建结构体指针
  9. s1 := new(Student)
  10. s1.name = "zs"
  11. s1.age = 12
  12. //s1就是一个指向结构体Student的指针,其类型为 *Student
  13. fmt.Printf("name=%s,age=%d", s1.name, s1.age)
  14. }

运行示例

在上述代码中,尽管s1的类型为*Student,但是我们仍然可以使用s1.name来访问name字段,而不是显式地取消引用指针(*s1).name来访问该字段。

匿名结构体

当然你也可以在不创建新数据类型的情况下声明结构。这些类型的结构称为匿名结构。匿名结构体没有类型名称,无需通过type关键字定义就可以直接使用。

  1. var_name := struct{
  2. member1 type1
  3. member2 type2
  4. }{key1:value1,key2:value2}

在上述代码中,这个结构体就称为匿名结构体,因为它只创建了一个新的结构体变量var_name并没有像命名结构体那样定义任何新的结构体类型。

匿名字段

可以使用仅包含类型而没有字段名称的字段创建结构。这些类型的字段称为匿名字段。

下面的代码中创建了一个结构体Student,其中有两个匿名字段string和int

  1. type Student struct{
  2. string
  3. int
  4. }

匿名字段没有明确的名称,默认情况下匿名字段的名称为其类型。例如在上面Student的情况下,虽然字段是匿名的,但默认情况采用字段数据类型名做为其字段名称。所以字段名称为int和string

嵌套结构体

结构体的一个字段可能是另一个结构体,这类型的结构体就是嵌套结构体。

例如

  1. package main
  2. import "fmt"
  3. type Address struct {
  4. city string
  5. }
  6. type Student struct {
  7. name string
  8. age int
  9. address Address
  10. }
  11. func main() {
  12. s := Student{
  13. name: "zs",
  14. age: 12,
  15. address:Address{
  16. city:"bj",
  17. },
  18. }
  19. fmt.Printf("name=%s,age=%d,city=%s",s.name,s.age,s.address.city)
  20. }

运行示例

当这个结构体字段是一个匿名字段时,其中的字段被称为提升字段。因为它们可以被直接访问到,就像它们属于持有匿名结构字段的结构一样。通过下面的例子更好的理解这个定义。

  1. package main
  2. import "fmt"
  3. type Address struct {
  4. city string
  5. }
  6. type Student struct {
  7. name string
  8. age int
  9. Address
  10. }
  11. func main() {
  12. s := Student{
  13. name: "zs",
  14. age: 12,
  15. Address:Address{
  16. city:"bj",
  17. },
  18. }
  19. fmt.Printf("name=%s,age=%d,city=%s",s.name,s.age,s.city)
  20. }

运行示例

在上述代码中,Student结构体有一个匿名字段Address,它是一个机构体。那么Address结构体的city字段就被称为提升字段。因为它们可以被直接访问,就好它直接被定义在Student结构当中一样。所以在代码中我们用s.city直接访问该字段。

结构体的比较

结构体是值变量,如果结构体内的每个字段都是可以比较的,那么这个结构体就是可以比较的。如果两个结构变量的对应的字段相等,则两个结构变量相等。

方法

方法(method)就是在func关键字和方法名之间具有一个特殊的接收器类型的函数。这个接收器可以是结构体或非结构体。

在调用方法时,

方法的定义

  1. func(t Type) methodName(paramter list){
  2. }

上述代码片段中,创建了一个名为methodName的方法,且接收器类型为Type的方法。t被称为接收者,可以在方法中访问t

方法和函数的区别

Go语言不是一种纯面向对象的语言,它不支持类。因此Go语言中的方法实现了一种类似于类行为的一种方式。方法允许对类型相关的逻辑行为进行分组,类似于类。

同名的方法可以定义在不同的类型(接收器)上,而不允许从在同名的函数。

在调用方法时,可以使用类型的实例值或者指针,编译器会自动根据接收器类型自动在值类型和指针类型之间转换。但是对于函数,你只能传递参数对应的类型,参数是值类型就是值类型,参数是指针类型就必须值指针类型。

示例:

  1. package main
  2. import "fmt"
  3. type Rectangle struct {
  4. length,width int
  5. }
  6. func (r Rectangle) Area() int {
  7. return r.length * r.width
  8. }
  9. func main() {
  10. r := Rectangle{10,5}
  11. r2 := &Rectangle{10,5}
  12. fmt.Printf("Area of rectangle %d\n", r.Area())
  13. fmt.Printf("Area of rectangle %d\n", r2.Area())
  14. }

运行结果

接收者类型问题

在方法中既可以接受值类型也可以接受指针类型。这两者之间的区别在与,指针类型接收器的方法内部所作的修改对调用者是可见的,而值类型则不然。

示例:

  1. package main
  2. import "fmt"
  3. type Rectangle struct {
  4. length, width int
  5. }
  6. func (r Rectangle) modify(len int) {
  7. r.length = len
  8. }
  9. func (r *Rectangle) modifys(len int) {
  10. r.length = len
  11. }
  12. func main() {
  13. r := Rectangle{10, 5}
  14. r.modify(1)
  15. fmt.Printf("修改没有发生%d\n", r.length)
  16. r.modifys(1)
  17. fmt.Printf("修改已经发生%d\n", r.length)
  18. }

何时使用值类型接收者,何时使用指针类型接收者?

  1. 粗暴的结论:如果你不知道怎么选择,那就使用指针。但有时候,使用值接收者会更合理,尤其是效率考虑,比如:不需要修改的小 struct、基础数据类型。以下是一些有用的指导方针:
  2. - 如果接收者是 mapfunc chan,不用使用指针。如果是 slice,并且方法不会 reslice 从分配 slice,不要使用指针;
  3. - 如果方法需要修改接收者,必须使用指针;
  4. - 如果接收者是包含了 sync.Mutex 或类似的同步字段的结构体(struct),接收者必须使用指针,避免拷贝;
  5. - 如果接收者是一个大的结构体或数组,使用指针会更高效。但多大是大?如果所有元素(struct 的字段或数组元素)作为方法的参数传递认为太大,那么作为接收者也是太大。(粗暴一些,所有元素内存超过指针大小,可以考虑使用指针);
  6. - 如果接收者是结构体、数组或 slice,同时,它们的元素是指针,且指向的数据要改变,那么使用指针接收者会更合理;(有点绕,那就总原则:用指针没错);
  7. - 如果接收者是小的数组,或小的没有可变字段或指针的结构体,或者结构体字段只是简单的基础类型,值接收者会更合理;值接收者能减少垃圾回收的压力,一般会优先分配在栈上(记住是一般,因为有可能逃逸);但除非有性能测试验证,否则别因为可以介绍垃圾回收压力,就选择值接收者;
  8. 最后再强调一下,如果你拿不定主意,那就用指针接收者。

Go语言基础六:结构体和方法的更多相关文章

  1. Go语言基础之结构体

    Go语言基础之结构体 Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念.Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性. 类型别名和自定义类型 自定义类型 在G ...

  2. GO学习-(13) Go语言基础之结构体

    Go语言基础之结构体 Go语言中没有"类"的概念,也不支持"类"的继承等面向对象的概念.Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性. ...

  3. Go语言基础之结构体(面向对象编程上)

    1 自定义类型和类型别名 1.1 自定义类型 Go语言中可以基于一些基本的数据类型,使用type关键字定义自定义类型的数据 . 自定义类型定义了一个全新的类型,该新类型具有基本数据类型的特性.自定义类 ...

  4. Go基础之--结构体和方法

    结构体的定义 结构体是将零个或者多个任意类型的命令变量组合在一起的聚合数据类型.每个变量都叫做结构体的成员. 其实简单理解,Go语言的结构体struct和其他语言的类class有相等的地位,但是GO语 ...

  5. C语言基础(19)-结构体,联合体,枚举和typedef

    一.结构体 1.1 结构体struct定义及初始化 #include <stdio.h> // 这个头文件在系统目录下 #include <stdlib.h> // 使用了sy ...

  6. go语言基础之结构体成员的使用指针变量

    1.结构体成员的使用:指针变量 示例: package main //必须有个main包 import "fmt" //定义一个结构体类型 type Student struct ...

  7. ndk学习之C语言基础复习----结构体、共用体与C++开端

    自己实现sprintf功能: 关于C中的系统函数sprintf在上次[https://www.cnblogs.com/webor2006/p/7545627.html]学习中已经用到过了,这里再来回顾 ...

  8. go语言基础之结构体做函数参数 值传递和地址传递

    1.结构体做函数参数值传递 示例: package main //必须有个main包 import "fmt" //定义一个结构体类型 type Student struct { ...

  9. go语言基础之结构体比较和赋值

    1.结构体比较和赋值 (同类型的结构体可以相互赋值) 示例: package main //必须有个main包 import "fmt" //定义一个结构体类型 type Stud ...

  10. go语言基础之结构体成员的使用普通变量

    1.结构体成员的使用普通变量 示例: package main //必须有个main包 import "fmt" //定义一个结构体类型 type Student struct { ...

随机推荐

  1. MySQL启动与多实例安装

    启动方式及故障排查 一.几个问题 1.1 /etc/init.d/mysql 从哪来 cp /usr/local/mysql/support-files/mysql.server /etc/init. ...

  2. CSS基础学习(二)

    11.CSS背景 ①设置背景颜色(颜色值通常可以用十六进制(如#000000)或者颜色名称(如red)来表示) 属性:background-color 例: body { background-col ...

  3. ML第5周学习小结

    本周收获 总结一下本周学习内容: 1.学习了<深入浅出Pandas>的第五章:Pandas高级操作的两个内容 数据迭代 函数应用 我的博客链接: pandas:数据迭代.函数应用 2.&l ...

  4. 阿里巴巴开源限流组件Sentinel初探

    1 Sentinel主页 https://github.com/alibaba/Sentinel/wiki/主页 1.1 Sentinel介绍 随着微服务的流行,服务和服务之间的稳定性变得越来越重要. ...

  5. Myers差分算法的理解、实现、可视化

    作者:Oto_G QQ: 421739728 目录 简介 基础 差异的描述 好的差异比较 算法介绍 名词解释 两个定理 绘制编辑图 感谢 简介 本文章对Myers差分算法(Myers Diff Alg ...

  6. Xmind头脑风暴

    下图导出到excel后 上图对应的excel表格如下:

  7. 一文掌握软件安全必备技术 SAST

    上一篇文章中,我们讨论了软件供应链的概念并了解到近年来软件供应链安全事件层出不穷.为了保障软件供应链安全,我们需要了解网络安全领域中的一些主要技术.本篇文章将介绍其中一个重要技术--SAST. 当开发 ...

  8. Javaweb-Servlet学习

    1.Servlet简介 Servlet就是sun公司开发动态web的一门技术 Sun在这些API中提供一个借口叫做:Servlet,如果你想开发一个Servlet程序,只需要完成两个小步骤: 编写一个 ...

  9. React关于constructor与super(props)之间的相爱相杀

    我们先把菜鸟教程的一段代码拿过来分析一下.下面这段代码是用了将生命周期方法添加到类中实现时钟效果. // 将生命周期方法添加到类中 class Clock extends React.Componen ...

  10. WPF开发随笔收录-唯一标识符GUID

    一.前言 该系列博客用于记录本人在WPF开发过程中遇到的各种知识点 二.正文 1.在工作的项目中,软件需要用到在线升级功能,由于第一次弄,在下载服务端的文件到本地时,文件的名称我选择直接生成为固定的格 ...