struct结构体(内容不含方法)

Go语言面向对象的介绍

Go语言用struct结构体来代替其它面向对象语言中的类(class)

Golang支持面向对象编程(oriented object programming, OOP), 但是和传统面向对象编程有区别,并不是纯粹的面向对象语言,因此我们说Golang支持面向对象编程特性

Golang去掉了传统OOP语言的方法重载、构造函数和析构函数,隐藏的this指针等等

Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样,比如继承:Golang没有extends关键字,继承是通过匿名字段来实现的

Golang中接口的概念非常重要,可以说,Golang中其实是面向接口编程

面向对象这个编程思想就不在这里提了,仅附上尚硅谷Go语言课程的图183_尚硅谷_Go面向对象编程快速入门_哔哩哔哩_bilibili

尚硅谷Go语言课程中的截图


我们将cat的属性抽象出来,放到cat的结构体进行抽象管理,

这就是一个简单使用,输出的是

结构体

结构体是自定义的数据类型,代表一类事物

结构体变量(实例)是具体的,实际的,代表一个具体变量

结构体中的字段,可以叫属性,英文一般称为field,就是指的name age等这些

字段是结构体的一个组成部分,一般是基本数据类型、数组,也可以是引用类型

字段/属性 的注意事项

①字段声明语法同变量

②字段的类型可以为:基本类型、数组、或引用类型

③在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值,也就是默认值

比如布尔类型是false,整型是0,字符串是“”,数组类型和其元素类型相关,指针,slice,和map的零值都是nil,即还没有分配空间,这个nil可以看成其它语言中的null

④不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另一个

看下面这个例子测试一下我们懂没懂这个意思

这里我们就会发现monster2改变,但是monster1不会改变,因为struct是值类型,进行的是值拷贝,它们所代表的地址是不一样的

创建结构体的四种方式

第一种:

先声明变量,再赋值,在上面已经说过了,详见猫猫公子的例子

第二种:

第三种,用new返回一个指针

这里还要提醒一下," . "的运算优先级要比“ * ”要高,因此(*p)一定要加括号!!!

第四种,用 “&” 取地址

Person{}已经向系统开辟了一块内存,生成了一个结构体变量,然后将这个结构体变量的地址赋给结构体指针变量

结构体变量在内存中的布局

在Golang中结构体是值类型,因为它是直接指向数据的,而不是指向一个地址,通过这个地址再指向数据

我们通过上面的例子也不难发现,结构体变量直接声明的时候系统就赋予了其默认值,没有通过make的方式向系统请求空间,因此这也间接说明了它是值类型的特征

下面有一个图可以比较直观的说明struct的内存分布:

尚硅谷Go语言课程中的截图


struct结构体的使用细节

结构体的所有字段在内存中是连续分布的,看下面这段代码会比较清楚

我们来看输出

我们可以看到在Point结构体中,x和y都是int,由于连续分布,因此x和y的地址之间差8字节

在Rect结构体中,每个point占16个字节,因此它们的地址相差16个字节,输出中是16进制的,转化成十进制就是16

这里再做一个思考,在声明结构体的时候,其实,内存大小已经分配好了,但是我们知道int系列和float系列,是可以提前分配内存的,但是,string类型和其它引用类型是不能提前分配内存的,这个时候结构体是怎么做到提前找到系统要到确切的内存呢?

我们再思考一下,string类型虽然是值类型,但是它底层其实还是引用了其它的byte数组

我们写一个程序来思考一下:

首先定义一个结构体:

下面是main函数中的内容

输出的结果是

unsafe.sizeof()函数可以查看变量的大小,单位是字节

对于引用类型map,在make之前(看13,14,15行),mapNo是默认值,也就是nil,这个时候我们调查nil的大小发现是8字节

当我们使用make(9,10行)给mapNo分配地址后,这个时候,再调查map的大小发现是8字节,后面就算插入了数值(11,12行),再调查map的大小发现还是8字节,因为map是引用类型,它引用的是别人的数值,而它自己就是一个地址,在64位操作系统中,地址是8字节的,因此map等引用类型就只是8字节

我们来看第5,6,7,8,9行,我们申请了*int的指针变量和*float64的指针变量,调查这两个地址变量的大小,我们可以看到,只要都是64位操作系统,那么,地址的大小总是64位,也就是8字节

最后看1,2,3,4行,fzd 和 lyc都是student类型的结构体,这个结构体中有两个string类型,对于string类型是16个字节的,string的前8个字节是引用的byte数组的地址,后8个字节是所引用的byte数组的长度。student结构体含有两个string,因此该结构体是16个字节的,又因为结构体变量是值类型,因此直接对变量进行size分析,它应该就是两个string的大小之和,也就是16个字节

②结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)

③结构体进行type重新定义后(相当于取别名),Golang会认为是新的数据类型,但是可以相互间强转

比如看下面这个

struct 如此, 如果我们type integer int 用在基本数据类型上也是一个道理

④struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化

序列化的意思就是把一个变量转化成字符串,这样易于传输

目前JSON比较常用

那么我们为什么要用这个tag呢?为了便于Golang语言与其它比如JQuery或者PHP及逆行交互,其它语言可能不习惯第一个字母大写。Go将第一个字母大写是为了其它包可用,但是其它语言没有这个搞法,它们没有将第一个字母大写的,这样在交互的时候就可能出问题

这个时候就用tag来解决

输出的内容是

在结构体中,Name和Altername一定要开头大写,要不然json.Marshal这个其它包的函数没有权限调用这个字段

json.Marshal(stu) 输入的是一个struct, 返回的是两个数据,第一个是byte类型的切片,第二个是是否转化成功

记得在使用的时候将切片转化为string

为了和其它语言的兼容性,使用了struct tag

我们可以看到,将struct转化为json字符串后,就可以正常输出小写开头的字段了

这个tag本质是用了反射,这个后面再来专门的学习