Go 结构体(Struct)
引用曾经看到的一篇文章里面对 Golang 中结构体的描述,如果说 Golang 的基础类型是原子,那么
结构体就是分子。我们都知道分子是由原子组成的,换言之就是结构体里面可以包含基础类型、切片、
字典、数组以及结构体自身。
结构体类型的定义
结构体定义的一般方式
type identifier struct {
field1 type1
field2 type2
...
}
结构体里的字段都有名字,而对于字段在代码中从来也不被使用到,那么可以命名它为_。对于相同类型的
字段我们可以使用简写的形式,比如:
type T struct {
a,b int
}
还有一个需要注意的地方是结构体中字段的大小写,首字母大写表示公有变量,首字母小写表示私有变量,
相当于其他语言类成员变量的Public、Private ,但是这个公有变量和私有变量在 Go 中是相对于 Package 来
说的,在同一个 Package 都是公有的。
结构体变量的创建
1 最常见的创建结构体变量形式
package main
import "fmt"
type Circle struct {
x int
y int
Radius int
}
func main() {
var c Circle = Circle{
x:100,
y:100,
Radius:50,
}
fmt.Println(c)
}
--------------------------------
{100 100 50}
通过显示指定结构体内部字段的名称和初始值初始化结构体,可以只指定部分字段的初始值,或一个都不指定,
那些没有被指定初始值的字段会自动初始化为相应类型的零值。
2 结构体变量创建的第二种形式是不显示指定字段而是根据字段顺序初始化,但是这样需要显示的提供所有字段的
初始值
package main
import "fmt"
type Circle struct {
x int
y int
Radius int
}
func main() {
var c Circle = Circle{100,100,50}
fmt.Println(c)
}
-------------------------------
{100 100 50}
3 使用 new() 函数创建结构体变量
结构体变量和普通变量都有指针形式,使用取地址符就可以得到结构体的指针类型
package main
import "fmt"
type Circle struct {
x int
y int
Radius int
}
func main() {
var c *Circle = &Circle{100,100,50}
fmt.Printf("%+v\n", c)
}
------------------------------------------
&{x:100 y:100 Radius:50}
下面看一下使用 new() 创建一个零值结构体,所有的字段都会被初始化成对应类型的零值
package main
import "fmt"
type Circle struct {
x int
y int
Radius int
}
func main() {
var c *Circle = new(Circle)
fmt.Printf("%+v\n", c)
}
-----------------------------------
&{x:0 y:0 Radius:0}
注意:new 函数返回的是指针类型
结构体的参数传递
看一个实例
package main import "fmt" // 声明一个结构体
type employee struct {
name,address string
height,weight float64
} // 定一个方法,该方法的参数是一个结构体,主要用于修改结构体成员中name的值
func modifyAttribute(emy employee) {
emy.name = "newer"
fmt.Println(emy)
} func main() {
// 初始化结构体
emy := employee{
name:"xiaoming",
address:"beijing",
height:172.0,
weight:75.3,
} // 打印修改前的值
fmt.Println(emy) // 调用modifyAttribute
modifyAttribute(emy) // 打印修改后值
fmt.Println(emy)
}
----------------------------------
输出结果
{xiaoming beijing 172 75.3}
{newer beijing 172 75.3}
{xiaoming beijing 172 75.3}
从上面的输出结果上来看,虽然在 modifyAttribute 方法中修改了 name 值,但是在 main 函数中打印 name 的
值并没有变化,说明这是一个值传递
我们把 modifyAttribute 函数的参数变成结构体的指针类型,如下
func modifyAttribute(emy *employee) {
emy.name = "newer"
fmt.Println(emy)
}
func main() {
// 初始化结构体
emy := employee{
name:"xiaoming",
address:"beijing",
height:172.0,
weight:75.3,
}
// 打印修改前的值
fmt.Println(emy)
// 调用modifyAttribute
modifyAttribute(&emy)
// 打印修改后值
fmt.Println(emy)
}
----------------------------
输出结果
{xiaoming beijing 172 75.3}
&{newer beijing 172 75.3}
{newer beijing 172 75.3}
我们看到在函数 modifyAttribute 中的修改影响到了 main 函数中的 name 值,这里是引用传递
再看一个例子:编写扩大圆半径的函数
package main import "fmt" // 定义一个结构体 Circle
type Circle struct {
x int
y int
Radius int
} // 通过值传递扩大圆半径
func expandByValue(c Circle) {
c.Radius *= 2
} // 通过引用传递扩大圆半径
func expandByPointer(c *Circle) {
c.Radius *= 2
}
func main() {
c := Circle{
Radius:50,
} expandByValue(c)
fmt.Println(c) expandByPointer(&c)
fmt.Println(c)
}
--------------------------------
输出结果
{0 0 50}
{0 0 100}
我们可以从上面的输出中再次看到通过值传递,在函数里面修改结构体的状态不会影响原有结构
体的状态,通过值传递就不一样。
结构体方法
Go 语言不是面向对象的语言,在 Go 语言中没有类的概念,结构体正是类的替代品。类可以附加很多成员方法,
结构体也可以。看一个实例,如何给结构体绑定方法:
package main import (
"fmt"
"math"
) // 定义一个结构体 Circle
type Circle struct {
x int
y int
Radius int
} // 计算圆的面积 第一个括号里面表示的是方法的接收者 这里方法的接收者是结构体 Circle
// Area() 表示函数名 float64 表示函数的返回值类型
func (c Circle) Area() float64{
return math.Pi * float64(c.Radius) * float64(c.Radius)
} // 计算圆的周长
func (c Circle) Circumference() float64 {
return 2 *math.Pi * float64(c.Radius)
} func main() {
// 初始化结构体
c := Circle{
Radius:50,
} fmt.Println(c.Area(),c.Circumference())
}
-------------------------------------------------
输出结果
7853.981633974483 314.1592653589793
结构体的指针方法
如果使用结构体方法的形式给 Circle 增加一个扩大圆半径的方法,会发现半径还是扩大不了
func (c Circle) expand() {
c.Radius *= 2
}
这个方法和前面的 expandByValue 函数是等价的,只是调整了一下第一个参数的位置,在参数传递的时候依然是值传递
,所以,还是无法起到扩大圆半径的作用,这个时候就需要使用结构体的指针方法
func (c *Circle) expand() {
c.Radius *= 2
}
结构体指针方法和值方法在调用上没有区别,只是一个可以改变结构体内部状态,另一个不会。另外指针方法可以使用结
构体变量调用,值方法也可以使用结构体指针变量使用
结构体变量调用指针方法(比如调用计算圆周长的方法):
package main import (
"fmt"
"math"
) // 定义一个结构体 Circle
type Circle struct {
x int
y int
Radius int
} // 计算圆的周长
func (c *Circle) Circumference() float64 {
c.Radius *= 2
return 2 *math.Pi * float64(c.Radius)
} func main() {
// 初始化结构体
c := Circle{
Radius:50,
}
fmt.Println(c.Circumference())
}
-----------------------------------------
输出结果
628.3185307179587
使用结构体指针变量调用值方法:
package main import (
"fmt"
"math"
) // 定义一个结构体 Circle
type Circle struct {
x int
y int
Radius int
} // 计算圆的面积
func (c Circle) Area() float64{
return math.Pi * float64(c.Radius) * float64(c.Radius)
} func main() {
// 初始化结构体
c := &Circle{
Radius:50,
} fmt.Println(c.Area())
}
---------------------------------
输出结果
7853.981633974483
内嵌结构体
结构体作为一种变量可以嵌套在另一个结构体中作为一个字段使用,这种内嵌结构体在 Go 语言中称之为
组合
package main import "fmt" // 定义一个结构体
type Pointer struct {
x int
y int
} func (p Pointer) show() {
fmt.Println(p.x,p.y)
} // 定义另一个结构体
type Circle struct {
// 将结构体 Pointer 嵌套在 Circle
loc Pointer
Radius int
} func main() {
c := Circle{
loc:Pointer{
x:100,
y:100,
},
Radius:50,
} fmt.Println(c)
fmt.Println(c.loc)
fmt.Println(c.loc.x,c.loc.y)
c.loc.show()
}
-----------------------------------
输出结果
{{100 100} 50}
{100 100}
100 100
100 100
匿名内嵌结构体
还有一种特殊的内嵌结构体形式,内嵌的结构体不提供名称。这时外面的结构体直接继承内嵌结构体的所有
内部字段和方法
package main import "fmt" // 定义一个结构体
type Pointer struct {
x int
y int
} func (p Pointer) show() {
fmt.Println(p.x,p.y)
} // 定义另一个结构体
type Circle struct {
// 匿名内嵌结构体
Pointer
Radius int
} func main() {
c := Circle{
Pointer:Pointer{
x:100,
y:100,
},
Radius:50,
} fmt.Println(c)
fmt.Println(c.Pointer)
fmt.Println(c.x,c.y) //继承字段
fmt.Println(c.Pointer.x,c.Pointer.y)
c.show() // 继承方法
c.Pointer.show()
}
--------------------------------------
输出结果
{{100 100} 50}
{100 100}
100 100
100 100
100 100
100 100
Go 语言的结构体没有多态性
我们知道面向对象语言的三大特性,封装、继承、多态。其中多态是指父类定义的方法可以调用子类实现的方法,不同的子类有不同的实现方法,
从而给父类的方法带来了多样的不同行为。而 Go 不是面向对象语言,它的结构体也不支持结构体,如下示例
package main import "fmt" // 定义一个fruit结构体
type Fruit struct { } func (f Fruit) eat() {
fmt.Println("eat Fruit")
} func (f Fruit) enjoy() {
fmt.Println("smell first")
f.eat()
fmt.Println("clean finally")
} // 定一个apple结构体
type Apple struct {
Fruit
} func (a Apple) eat() {
fmt.Println("eat apple")
} // 定义一个banana结构体
type Banana struct {
Fruit
} func (b Banana) eat() {
fmt.Println("eat banana")
} func main() {
apple := Apple{}
banana := Banana{} apple.enjoy()
banana.enjoy()
}
-----------------------------
输出结果
smell first
eat Fruit
clean finally
smell first
eat Fruit
clean finally
从上面的输出结果中可以看到,虽然外部结构体可以继承内部结构体的方法和字段,但是外部结构体的方法不能覆盖内部结构体的方法,
enjoy 方法调用的 eat 方法还是 Fruit 自己的 eat 方法,它并没有被外面结构体的方法覆盖掉
Go 结构体(Struct)的更多相关文章
- C语言结构体-struct
知识点: 1)结构体的定义. 2)结构体的sizeof. 3) 结构体的指针. 1) 结构体的定义: 在逻辑上有一定关联的多个数据类型做为一整体进行操作的数据结构,它的关键字是struct.下面我将 ...
- 内核中用于数据接收的结构体struct msghdr(转)
内核中用于数据接收的结构体struct msghdr(转) 我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构.在我们的虚拟机上发送icmp回显请求包,pin ...
- Swift中元组(Tuples),结构体(Struct),枚举(Enums)之间的区别
Swift有许多种存储数据方式,你可以用枚举(enums),元组(tuples),结构体(structs),类(classes),在这篇文章中我们将比较枚举.元组.结构体之间区别,首先从最简单的开始- ...
- C语言 - 结构体(struct)比特字段(:) 详细解释
结构体(struct)比特字段(:) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26722511 结构体(struc ...
- Go语言学习笔记(四)结构体struct & 接口Interface & 反射
加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套: go中的struc ...
- 结构体(struct)大小
结构体(struct)大小 本文参考链接:C语言结构体(struct)常见使用方法,链接中的实例代码经实践有几处不准确,本文在引用时已做更改 注意:在结构体定义时不能申请空间(除非是结构体变量),不可 ...
- 结构体struct sockaddr_in, struct sockaddr,struct in_addr
一.结构体 struct sockaddr_in, struct sockaddr, struct in_addr struct sockaddr_in, struct sockaddr,str ...
- GO开发[五]:golang结构体struct
Go结构体struct Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性 ...
- 结构体struct、联合体union、枚举类型enum
1.c语言中的类型 1)内置类型——char,short,int,float,double: 2)用户自定义类型(UDT)——struct结构体,union联合体,enum枚举类型 2.内存对齐 2. ...
- C++/C#:类Class与结构体Struct的区别
C++中: 默认的访问控制.继承访问权限不同:struct时public的,class时 private的: 其它基本一样. C#中: struct是值类型,class是引用类型的: struct S ...
随机推荐
- 阻塞队列---ArrayBlockingQueue,LinkedBlockingQueue,DelayQueue源码分析
阻塞队列和非阻塞队列阻塞队列和非阻塞队列的区别:阻塞队列可以自己阻塞,非阻塞队列不能自己阻塞,只能使用队列wait(),notify()进行队列消息传送.而阻塞队列当队列里面没有值时,会阻塞直到有值输 ...
- <转>jmeter(十)参数化
本博客转载自:http://www.cnblogs.com/imyalost/category/846346.html 个人感觉不错,对jmeter讲解非常详细,担心以后找不到了,所以转发出来,留着慢 ...
- 一线互联网常见的 14 个 Java 面试题,你颤抖了吗程序员
跳槽不算频繁,但参加过不少面试(电话面试.face to face 面试),面过大 / 小公司.互联网 / 传统软件公司,面糊过(眼高手低,缺乏实战经验,挂掉),也面过人,所幸未因失败而气馁,在此过程 ...
- mycat高可用集群搭建
本文来源于:https://blog.csdn.net/u012758088/article/details/78741567 Mycat 本身是无状态的,可以用 HAProxy 或四层交换机等设备组 ...
- linux下nginx编译安装
步骤: 1.获取nginx安装包. 进入nginx官网:http://nginx.org/ 找到稳定版本: 点击红框内的链接. 使用wget获取安装包. wget http://nginx.org/d ...
- leetcode [34] Find First and Last Position of Element in Sorted Array
Given an array of integers nums sorted in ascending order, find the starting and ending position of ...
- crontab命令的使用方法
crontab命令常见于Unix和Linux的操作系统之中,用于设置周期性被执行的指令.该命令从标准输入设备读取指令,并将其存放于"crontab"文件中,以供之后读取和执行. 在 ...
- js条件判断if-else和switch、循环for和while
条件判断和循环都使用{ }将代码块括起来,如果代码块只有一行,则可省略{ }. 在循环中,continue表示跳过当前循环继续进行下一次循环,break表示跳出整个循环. 1.条件判断if-else, ...
- Python3 根据m3u8下载视频,批量下载ts文件并且合并
Python3 根据m3u8下载视频,批量下载ts文件并且合并 m3u8是苹果公司推出一种视频播放标准,是一种文件检索格式,将视频切割成一小段一小段的ts格式的视频文件,然后存在服务器中(现在为了减少 ...
- opencv学习之路(8)、基本图像运算——加减与或
一.图像加法 #include<opencv2/opencv.hpp> #include<iostream> using namespace cv; using namespa ...