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 ...
随机推荐
- SQL优化(转)
1. 负向条件查询不能使用索引 select * from order where status!=0 and stauts!=1 not in/not exists都不是好习惯 可以优化为in查询: ...
- python URLError,HTTPError 的异常处理
URLError,HTTPError 的异常处理 1. URLErrorURLError产生的原因1). 网络无连接2). 连接不到特定的服务器3). 服务器不存在 # 例子 import urlli ...
- 前端框架VUE----es6简单介绍
1.ECMAScript 6 简介 ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了.它的目标,是使得 JavaScr ...
- MD5加密算法Java代码实现
package com.app.utils; import java.math.BigInteger; import java.security.MessageDigest; import java. ...
- Maven笔记 #01# 入门
索引 Maven是干什么的? 用Maven的好处 Maven与命令行 Maven与IntelliJ IDEA 一.Maven是干什么的? 我相信只要你写过足够多的代码,就... 肯定有思考过写一个脚本 ...
- 我是这样做APP的:击中用户的痛点(转)
击中用户的痛点 点评,感觉取名叫做“用户痛点的取舍”更加合适.很多公司.项目的失败完全取决于决策人取舍的失败,一味地追求大而全.迎合上级领导,专断而没有和团队做客观的分析.本文虽然以一个应该来说并不复 ...
- CAN通信工作原理个人心得
CAN总线结构示意图: 说明: 1:CAN收发器(示意图中的单元)根据两总线CAN_H和CAN_L的电位差来判断总线电平: 2:实际中CAN_H与CAN_L由双绞线组成: 3:数据传递终端的电阻器,是 ...
- H5、React Native、Native性能区别选择
“存在即合理”.凡是存在的,都是合乎规律的.任何新事物的产生总要的它的道理:任何新事物的发展总是有着取代旧事物的能力.React Native来的正是时候,一则是因为H5发展到一定程度的受限:二则是移 ...
- 翻硬币|2013年蓝桥杯B组题解析第八题-fishers
翻硬币 小明正在玩一个"翻硬币"的游戏. 桌上放着排成一排的若干硬币.我们用 * 表示正面,用 o 表示反面(是小写字母,不是零). 比如,可能情形是:oooooo 如果同时翻转左 ...
- Windows环境下32位汇编语言程序设计笔记-基础篇
内存模式 .386 .model flat,stdcall ;子程序调用模式,win32中只能用stdcall,因为win32api调用使用的这个 option casemap:none ;定义了程序 ...