Go语言实践[回顾]教程27--详解Go语言结构体 struct 定义与面向对象编程思想

  在 Go 语言中没有其他常用语言中“类”的概念,那么面向对象编程的理念是不是就无法在 Go 语言中应用呢?答案是可以,虽然没有“类”,但是取而代之的是“结构体 struct”。Go 语言依据强大的类型系统,使用结构体可以构建各种新的数据类型,再配以其方法,就可以灵活实现各种对象所需的基础定义。这种方式简单干净,没有为了实现继承和多态而额外添加虚拟函数指针,尽量减少相互关联,多态则由接口来实现,具有简单、平坦、直接、易懂、易记等鲜明特点。

  通过 结构体 struct 来自定义数据类型是 Go 语言面向对象编程的基础,所以熟练使用 结构体 struct 是十分必要的,这一节开始我们就对 结构体 struct 展开实践。

结构体 struct 类型的自定义

  使用 结构体 struct 可以构造出复杂、多样的数据结构,所以使用 结构体 struct 自定义数据类型在 Go 语言编程中应用较为普遍,同时也是 Go 语言类型扩展的基石,更是 Go 语言面向对象编程的基础。结构体 struct 属于复合类型,是由零至多个任意类型的值聚合构成的,每个值都可以称为结构体的成员。结构体 struct 定义格式如下:

type 自定义类型名 struct {
    成员1名 成员1类型
    成员2名 成员2类型
    ......
}

  type:声明定义新的数据类型的关键字;
  自定义类型名:自己定义的结构体的名称,也就是一个新类型的名称,在同一个包内不能重复。
  struct{}:表示定义一个结构体类型,struct 是定义结构体的关键字;
  成员1名、成员2名……:表示结构体包含的成员名,结构体中的成员名必须唯一。
  成员1类型、成员2类型……:表示结构体各个成员的数据类型。
  上面整体可以理解为将 struct{} 结构体定义为 自定义类型名 的类型。

// test01 项目的 main 包,文件名 main.go
package main

import (
	"fmt"
)

type ball struct {          // 自定义一个 ball 数据类型
	name string             // 成员 name
	diam int                // 成员 diam
}

// 主函数,程序入口
func main() {
	a := ball{name:"小球", diam:20}   // 实例化 ball 类型,变量 a 则为一个对象,它具有 name、diam 两个属性
	fmt.Println(a)
}

  上述代码编译执行结果如下:

{小球 20}

  第8~11行定义了一个球 ball 数据类型,其有 name、diam 两个成员,分别代表名字和直径,这只是定义了这个类型中的数据结构,也就是数据类型内部组织形式。通过 struct 自定义的数据类型属于引用类型,在未实例化之前,是不能使用的。第15行就是实例化的一种方式,变量 a 就是 ball 的一个实例,其属性 name 的值是 “小球”,属性 diam 的值是 20。

  上面演示的都是使用的局部变量,如果要定义公共变量,使用 var 关键字及其相应的标准格式即可,格式与声明其它基本数据类型的变量时一样,这里就不在多描述。

结构体 struct 类型的匿名成员

  如果在结构体定义时,某个成员只给了数据类型,没有成员名,那么这个成员就是匿名成员,准确的说他的名字就是类型名。

// test01 项目的 main 包,文件名 main.go
package main

import (
	"fmt"
)

type ball struct {
	name string
	int            // 匿名成员,只有类型名
}

// 主函数,程序入口
func main() {
	a := ball{name: "小球", int: 20}

	fmt.Println(a)
}

  上述代码编译执行结果如下:

{小球 20}

  第10行没有写成员名,直接写了一个 int 数据类型名,这样的定义,就等于定义了一个名字为 int 的成员,其数据类型也是 int。

  匿名成员同样要遵守命名唯一原则,在同一个结构体内不能出现多个同类型的匿名成员。如果是个自定义类型,且来自其它的包,那要带上包名(必须是在该包内是允许外部访问的类型)。

结构体 struct 类型的实例化与初始化

  结构体 struct 的定义只是描述了数据结构,并没有分配数据内存,只有将该结构类型实例化时,才会为其真正分配内存,因此在结构体定义后实例化之前,是不能使用结构体内定义的成员的。

  实例化过程是按照自定义的数据机构创建一份与其格式一样的内存区域,所以多次实例化,就会出现完全独立的多个实例,也就是相当于其他语言中的“对象”。下面实例化后的结构体变量我都用对象来描述。

  实例化时可以同时初始化,也可以实例化后再初始化,Go 语言对结构体的实例化与初始化有多种方式与格式。

// test01 项目的 main 包,文件名 main.go
package main

import (
	"fmt"
)

type ball struct {           // 自定义一个 ball 数据类型
	name string
	diam int
}

// 主函数,程序入口
func main() {
	// 第一种实例化初始化方法,按照定义时的成员顺序进行初始化
	a1 := ball{"小球a1", 20} // 书写格式1
	a2 := ball{              // 书写格式2
		"小球a2",
		20,
	}
	a3 := ball{              // 书写格式3
		"小球a3",
		20}

	// 第二种实例化初始化方法,指定成员名进行初始化
	b1 := ball{name: "小球b1", diam: 20}   // 书写格式1
	b2 := ball{                            // 书写格式2
		name: "小球b2",
		diam: 20,
	}
	b3 := ball{                            // 书写格式3
		name: "小球b3",
		diam: 20}

	// 第三种实例化初始化方法,先实例化并初始化成员为零值,再按需要逐一成员赋值
	c := ball{} // 实例化并初始化各成员为零值
	c.name = "小球c"
	c.diam = 20

	// 第四种实例化初始化方法,引用地址实例化,先实例化并初始化成员为零值,再按需要逐一成员赋值
	d := new(ball)                 // 实例化为地址引用,并初始化各成员为零值
	d.name = "小球d"
	d.diam = 20

	// 第五种实例化初始化方法,同第四种
	e := &ball{}                  // 实例化为地址引用,并初始化各成员为零值
	e.name = "小球e"
	e.diam = 20

	fmt.Println(a1)
	fmt.Println(a2)
	fmt.Println(a3)
	fmt.Println(b1)
	fmt.Println(b2)
	fmt.Println(b3)
	fmt.Println(c)
	fmt.Println(d)
	fmt.Println(e)
}

  上述代码编译执行结果如下:

{小球a1 20}
{小球a2 20}
{小球a3 20}
{小球b1 20}
{小球b2 20}
{小球b3 20}
{小球c 20}
&{小球d 20}
&{小球e 20

  第一种是实例化的同时直接初始化,按照结构体定义时的成员(实例化后叫属性更好)排列顺序,将相应的成员值按照顺序依次排列在花括号内。a1、a2、a3 的实例化初始化属于同一种,只是书写语法格式不同而已。这种不用书写成员名,但是如果成员较多的时候,顺序容易搞错。

  第二种与第一种类似,也是实例化的同时直接初始化,区别是不用按照顺序初始化赋值,依靠成员名给相应的成员赋值。b1、b2、b3 的实例化初始化属于同一种,只是书写语法格式不同而已。这种不用记住成员顺序,只要记得成员名就可以赋值了。

  第三种是先实例化并初始化一个成员值都是空值的对象,然后再根据需要给定的最初值对其成员逐一赋值。如果在第36行后面直接打印输出对象 c,将得到成员都是空值的 “{ 20}”。第37行是为对象 c 的成员 name 赋值为 “小球c”,第38行是为对象 c 的成员 diam 赋值为 20。对象.成员 是指定成员的语法格式。

  第四种是使用 new() 内置函数进行实例化初始化的,也是先实例化并初始化一个成员值都是空值的对象,与第三种不同的是 new() 实例化的对象是引用的对象地址,所以打印输出结果中带 & 符号。之后成员的赋值与第三种并无不同。

  第五种使用 & 符号实例化,内部会自动触发 new() 函数,所以特点与第四种完全一样。

结构体 struct 使 Go 语言不用类也可以面向对象编程

  结构体的成员可以是任何的类型,基本类型、复合类型、接口、函数、指针乃至结构体本身都可以。这样结构体可以定义出复杂多样的各种数据结构,再为其赋予方法和接口(有关方法和接口在后面章节介绍),那么就有了行为,就是一个完整意义的对象了。所以 Go 语言没有提供类的概念,并不是不支持面向对象编程,而是使用了结构体从另一个途径实现了。具体编程操作后面章节会有深入实践。

上一节:Go语言实践[回顾]教程26–详解Go语言函数的闭包

下一节:待续…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学为所用

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值