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 ...
随机推荐
- Linux基础命令---显示树形进程pstree
pstree pstree显示正在运行的进程的树形结构,树以PID为根:如果省略了pid则以init为根.如果指定了用户名,则显示根植于该用户拥有的进程的所有进程树.如果pstree被调用为pstre ...
- urlopen和urlretrieve
import urllib import re url = "https://www.duitang.com/search/?kw=%E9%AC%BC%E6%80%AA&type=f ...
- SharePoint 解决管理员密码修改后各种问题的来袭
问题描述:本人用的是Win10自带虚拟机Hyper-V.Windows Service 2012R2.SQL2014和SharePoint2016,由于有一段时间没有登录虚拟机,在此登录的时候提示密码 ...
- ECharts + Jquery 做大屏展示
HTML <!doctype html> <html> <head> <meta charset="utf-8"> <meta ...
- body中相关标签
一.字体标签 字体标签包含:h1~h6.<font>.<u>.<b>.<strong><em>.<sup>.<sub> ...
- Scrapy小技巧-MySQL存储, MYSQL拼接
这两天上班接手,别人留下来的爬虫发现一个很好玩的 SQL脚本拼接. 只要你的Scrapy Field字段名字和 数据库字段的名字 一样.那么恭喜你你就可以拷贝这段SQL拼接脚本.进行MySQL入库处理 ...
- TCP 之 RST 原因分析
5. 往一个对端已经关闭的套接字上写入数据会收到一个RST信号 1.发送端的 发送缓冲区还有数据,但接收端tcp的接收通道已关闭 2. SYN到达某端口但此端口上没有正在监听的服务器.对于UDP,当一 ...
- 一致性哈希算法(适用于分库分表、RPC负载均衡)转
在分布式应用中,应该来说使用到hash最多的地方就是rpc负载均衡和分库分表,通常对于正式意义上的分布式应用来说,扩容和收缩是一个半自动化的过程,在此期间,应用基本上是可用的,所以不能发生大规模动荡的 ...
- 《web前端设计基础——HTML5、CSS3、JavaScript》 张树明版 简答题简单整理
web前端设计基础——HTML5.CSS3.JavaScript 简答题整理 第一章 (1)解释一下名词的含义:IP地址.URL.域名 iP定义了如何连入因特网,以及数据如何在主机间传输的标准. ...
- 01:adminLTE2基本使用
1.1 adminLTE介绍 1.adminLTE 介绍 1.基于Bootstrap3高度可定制的响应式管理模板,免去前端架构师大量的js和css的编写 2.adminLTE除了可以使用bootstr ...