11 go语言(golang) - 数据类型:结构体

结构体

结构体(struct)是一种自定义数据类型,它允许你将多个不同或相同类型的变量组合成一个单一的类型,类似于其他编程语言中的“类”。

结构体是 Go 语言中实现面向对象编程特性的一种方式,结构体但Go语言是面向过程的,没有类和继承的概念。

1、定义结构体

一个结构体由一组字段(field)组成,每个字段都有一个名称和类型。可以通过type关键字来定义一个新的结构体类型。结构体的字段可以是任何类型,包括基本类型、其他结构体、切片、哈希表等。

type Product struct {name  stringprice float64
}

2、创建结构体实例

可以使用两种方式来创建结构体实例:直接声明或使用 new 函数。

func Test1(t *testing.T) {product1A := Product{Name: "苹果"} // 使用字面量创建结构体,不指定字段名,Go会自动分配默认值product1B := Product{Name: "苹果", Price: 3.98}product1C := Product{"苹果", 3.98}  // 省略字段名product1D := &Product{"苹果", 3.98} // 获取指针类型fmt.Println(product1A)fmt.Println(product1B)fmt.Println(product1C)fmt.Println(product1D)fmt.Println("=========")product2A := new(Product) // new创建空结构体,返回指针类型product2A.Name = "手机"product2A.Price = 4999.99product2B := *new(Product) // 解引product2B.Name = "手机"product2B.Price = 4999.99fmt.Println(product2A)fmt.Println(product2B)
}

输出:

{苹果 0}
{苹果 3.98}
{苹果 3.98}
&{苹果 3.98}
=========
&{手机 4999.99}
{手机 4999.99}

3、嵌套与匿名字段

Golang支持嵌套其他结构体作为字段,并且允许匿名嵌入,这类似于继承的一种实现方式。如果结构体字段的类型名称前没有字段名,那么该字段就是匿名字段。

type NewProduct struct {ProductBrand string
}func Test2(t *testing.T) {product := NewProduct{Product: Product{Name:  "手机",Price: 4999.99,},Brand: "oppp",}fmt.Println(product)fmt.Println(product.Name)fmt.Println(product.Price)fmt.Println(product.Brand)
}

输出:

{{手机 4999.99} oppp}
手机
4999.99
oppp

4、函数类型字段

结构体内部不能直接定义函数,但可以包含函数类型的字段。这意味着你可以在结构体中声明一个字段,其类型是一个函数签名。这个字段本质上是一个指向某种特定签名的“回调”或“处理器”。

// 定义一个结构体,其中包含一个函数类型的字段
type Student2 struct {num      intname     stringsayHello func() string // 函数类型字段,用于存储具有特定签名的函数
}func Test(t *testing.T) {p := Student2{num:      1,name:     "小明",sayHello: func() string { return "我的名字是?" }, // 将匿名函数赋值给sayHello字段}str := p.sayHello() // 调用存储在sayHello中的具体实现fmt.Println("sayHello:", str)
}

理解与应用

  • 灵活性:通过这种方式,你可以为不同实例提供不同功能,实现类似策略模式(Strategy Pattern)的效果。
  • 使用场景:通常用于需要动态行为配置或回调机制的数据模型设计。

注意:这是一种灵活但独立于具体数据模型之外的方法配置方式。它无法直接引用或操作同一数据模型内的其他成员。

5、结构体的方法

函数类型字段本身不能直接引用结构体的其他字段,因为它们只是简单的函数指针,没有绑定到特定的结构体实例上。然而,你可以通过方法(method)来实现对结构体字段的访问。

在 Go 中,方法的定义与普通函数类似,但需要在 func 关键字和方法名之间指定接收者(receiver)。接收者可以是值接收者或指针接收者。

5.1、值接收者

当一个方法的接收者是一个值类型时,该方法会对调用它的结构体实例进行值复制。也就是说,方法内部对该结构体字段的任何修改都不会影响原始实例。

func Test1(t *testing.T) {student := Student{name: "小明", num: 12}student.sayHello()fmt.Println(student.num)
}type Student struct {num  intname string
}func (s Student) sayHello() {fmt.Printf("大家好,我叫%s \n", s.name)// 值接受者,不会对原数据进行修改s.num = 99
}

特点:

  • 方法调用时,会复制整个结构体。
  • 不会修改原始数据。
  • 适用于小型数据结构,因为复制开销较小。

输出:

大家好,我叫小明 
12

5.2、指针接收者

当一个方法的接收者是指针类型时,该方法会直接操作传入对象的内存地址。因此,任何对字段进行修改的方法都会影响到原始实例。

func Test2(t *testing.T) {student := Student{name: "小明", num: 12}student.sayHello2()fmt.Println(student.num)
}func (s *Student) sayHello2() {fmt.Printf("大家好,我叫%s \n", s.name)// 会对原数据进行修改s.num = 99
}

特点:

  • 方法调用时,不会复制整个结构体,而是传递内存地址。
  • 可以修改原始数据。
  • 更高效地处理大型数据结构,因为避免了不必要的数据拷贝。
  • 必须使用指针才能实现某些接口(如果接口要求的是指针)。

输出:

大家好,我叫小明 
99

5.3、如何选择?

  1. 需要修改对象状态:如果你的方法需要改变对象本身,比如更新某个字段,那么应该使用指针接收者。

  2. 避免拷贝开销:对于包含大量数据或复杂嵌套的数据类型,使用指针可以避免每次调用都进行完整的数据拷贝,提高性能。

  3. 一致性考虑:为了保持一致性,即使某些不需要改变状态的方法也可能选择用指针,以便与其他需要改变状态的方法保持一致。在同一类型上混合使用值和指针作为接受器可能导致代码难以理解,因此通常建议统一采用一种方式。

  4. 简单只读操作: 如果你的函数只是读取而不更改对象,可以考虑用值接受器,但前提是这个struct比较小且简单,这样做能让代码更直观。

5.4、与普通函数的区别

这两种函数定义的主要区别在于方法接收者和普通函数的使用方式以及语义上的差异。

方法(Method)
func (s Student) sayHello() {fmt.Printf("大家好,我叫%s \n", s.name)// 值接受者,不会对原数据进行修改s.num = 99
}
  • 接收者(s Student)是一个值接收者,这意味着当调用这个方法时,会对Student实例进行值复制。
  • 调用方式:这种形式是将sayHello()作为类型Student的方法,可以通过该类型的实例来调用
  • 语义上:将函数与特定的数据结构绑定,表示该操作与数据结构紧密相关,通常用于实现接口或增强面向对象风格。
普通函数
func sayHello3(s Student){fmt.Printf("大家好,我叫%s \n", s.name)
}
  • 参数传递:这里的参数传递也是按值传递,即对参数进行复制。
  • 调用方式:这是一个独立的函数,与任何类型无关。你需要显式地传入一个Student实例来调用它
  • 语义上:表示这是一个独立于数据结构之外的功能,可以应用于任何符合参数要求的数据,而不必是某个特定类型的方法。
使用
  1. 如果你希望某个功能紧密绑定到某个数据类型,并且可能会用到接口实现,那么选择方法。

  2. 如果功能相对独立,不需要与具体的数据结构强关联,或者可能适用于多种不同的数据结构,那么选择普通函数。

  3. 在设计API时,如果希望提供一种更自然、更面向对象风格的接口,则可以考虑使用方法。否则,对于工具类、辅助类逻辑,普通函数可能更加合适。

6、结构体的比较

在Go语言中,结构体的比较是一个重要的特性。结构体可以通过比较运算符进行相等性测试,但需要满足一定条件。

可比较的条件

  1. 可比较字段:只有当结构体中的所有字段都是可比较类型时,整个结构体才是可比较的。基本数据类型(如整数、浮点数、布尔值、字符串)和指针都是可直接进行相等性测试的。

  2. 不可直接比较字段:如果一个结构体包含切片(slice)、映射(map)、函数(function)或其他不支持直接相等运算符的数据类型,那么这个结构体不能使用==!=进行整体上的相等性测试。

可直接比较
type Point struct {X, Y int
}func main() {p1 := Point{X: 1, Y: 2}p2 := Point{X: 1, Y: 2}p3 := Point{X: 3, Y: 4}fmt.Println(p1 == p2) // 输出:true,因为所有字段都相同fmt.Println(p1 == p3) // 输出:false,因为字段不同
}
不可直接比较
type Data struct {Numbers []int // 切片是不可以用==来做整体对比的
}type Student struct {num      intname     stringsayHello func() string
}func Test2(t *testing.T) {d1 := Data{Numbers: []int{1, 2}}d2 := Data{Numbers: []int{1, 2}}// 以下代码会导致编译错误:// invalid operation: d1 == d2 (struct containing []int cannot be compared)//fmt.Println(d1 == d2)fmt.Println(d1)fmt.Println(d2)var s1 = Student{1, "小明", func() string { return "x" }}var s2 = Student{1, "小明", func() string { return "x" }}// 以下代码会导致编译错误:// invalid operation: s1 == s2 (struct containing func() string cannot be compared)//fmt.Println(s1 == s2)fmt.Println(s1)fmt.Println(s2)
}

自定义Equal方法

对于包含不可比元素的数据类型,你可以通过自定义方法来实现自己的“相等”逻辑。例如,可以为切片逐个元素地进行对比:

// Equal 方法用于判断两个 Student 是否“相等”
func (s1 Student) Equal(s2 Student) bool {if s1.num != s2.num {return false}if s1.name != s2.name {return false}return true
}func Test3(t *testing.T) {var s1 = Student{1, "小明", func() string { return "x" }}var s2 = Student{1, "小明", func() string { return "x" }}println(s1.Equal(s2)) // 输出true
}

注意事项

  • 指针与零值:即使某些字段是指针,只要它们所指向的数据本身是可比的,并且没有出现nil解引用的问题,它们也是可以参与整体对比操作。

  • 性能考虑:对于大型数据集或复杂嵌套的数据,手动实现对比可能会影响性能,因此在设计时应考虑到这一点。

思考

1、结构体声明时,可以指定默认值吗

结构体声明时不能直接指定默认值。结构体的字段在实例化时会自动初始化为其类型的零值。

不过,我们可以通过构造函数模式来实现类似于默认值的效果。

type InitStruct struct {a stringb int
}/*
定义一个构造函数,为结构体赋初始默认值
*/
func DefaultInit() InitStruct {return InitStruct{a: "默认值", b: 99}
}func Test3(t *testing.T) {initStruct := DefaultInit()fmt.Println(initStruct)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/469662.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《重学Java设计模式》之 原型模式

原型模式主要解决的问题就是创建重复对象,而这部分对象内容本身比较复杂,生成过程可能从库或者RPC接口中获取数据的耗时较长,因此采用克隆的方式节省时间。 案例:上机考试抽题,要求打乱题目、答案数据 工厂结构 选择题…

Java项目实战II基于Spring Boot的药店管理系统的设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着医疗行业的快速发展和人们对健康需…

html+js+css实现拖拽式便签留言

前些日子在网上冲浪时,看到一个便签式留言墙,让人耳目一新。心想这个看着不错,额想要。于是便开始搜寻是否有相应开源插件,想将其引入自己的博客中。但是搜寻了一圈,都没有符合预期的,要么功能不符合。有的功能符合&am…

模型压缩相关技术概念澄清(量化/剪枝/知识蒸馏)

1.模型压缩背景 随着深度学习技术的不断发展,模型的规模和复杂度也随之增加。大型模型往往具有更高的精度和更强的泛化能力,但在实际应用中,模型的大小却成为了一个制约因素。模型体积过大会导致存储、传输和推理速度等方面的瓶颈&#xff0…

Linux入门:环境变量与进程地址空间

一. 环境变量 1. 概念 1️⃣基本概念: 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如:我们在编写C/C代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里&#x…

Mysql前言

文章目录 Mysql 数据库简介SQL 基础语法什么是 SQL语句SQL 的作用SQL 语句的分类SQL 通用语法查询状态 🏡作者主页:点击! 🤖Mysql专栏:点击! ⏰️创作时间:2024年11月12日18点20分 SQL是数据库…

VCSVerdi:KDB文件的生成和导入

相关阅读 VCShttps://blog.csdn.net/weixin_45791458/category_12828763.html Verdihttps://blog.csdn.net/weixin_45791458/category_12829428.html?spm1001.2014.3001.5482 前言 在复杂的设计中,很难在HDL或测试平台级别(如使用系统函数&#xff…

2024年【汽车修理工(高级)】考试试卷及汽车修理工(高级)证考试

题库来源:安全生产模拟考试一点通公众号小程序 汽车修理工(高级)考试试卷是安全生产模拟考试一点通总题库中生成的一套汽车修理工(高级)证考试,安全生产模拟考试一点通上汽车修理工(高级&#…

灵活就业,真的等同于失业吗?“三无人员”如何齐短板获贷款

现在灵活就业的人越来越多,目前有约2亿人选择灵活就业,今天咱们就来好好聊聊,灵活就业,它真的等同于失业吗? 咱们可以看看那些跑外卖的、做网约车司机的,虽然他们看起来在忙忙碌碌地工作,但细究…

python识别ocr 图片和pdf文件

#识别图片 pip3 install paddleocr pip3 install paddlepaddle#识别pdf pip3 install PyMuPDF 重点:路径不能有中文,不然pdf文件访问不了 from paddleocr import PaddleOCR from rest_framework.response import Response from rest_framework.views im…

由于找不到mfc120u.dll, 无法继续执行代码。重新安装程序可能解决引问题。

运行MFC程序报下面错误,无法到找运行库mfc120u.dll msvcr120.dll也找不到 下载C++运行库安装程序 mfc12对应2013运行库 运行库安装成功

介绍和安装及数据类型

1、介绍和安装 1.1、简介 ClickHouse是俄罗斯的Yandex于2016年开源的列式存储数据库(DBMS),使用C语言编写,主要用于在线分析处理查询(OLAP),能够使用SQL查询实时生成分析数据报告。 OLAP&…

【Pikachu】越权访问实战

所谓理想,只是同时拥有实力的人才能说的“现实”。所谓弱就是一种罪。 1.Over Permission概述 如果使用A用户的权限去操作B用户的数据,A的权限小于B的权限,如果能够成功操作,则称之为越权操作。 越权漏洞形成的原因是后台使用了…

KubeVirt入门介绍

KubeVirt入门介绍 KubeVirt 是一个开源项目,旨在通过 Kubernetes 管理虚拟机(VM),使得 Kubernetes 不仅支持容器化工作负载,还支持虚拟机的部署和管理。这种双重支持的目标是提供一个统一的云原生平台,让开…

分布式----Ceph部署

目录 一、存储基础 1.1 单机存储设备 1.2 单机存储的问题 1.3 商业存储解决方案 1.4 分布式存储(软件定义的存储 SDS) 1.5 分布式存储的类型 二、Ceph 简介 三、Ceph 优势 四、Ceph 架构 五、Ceph 核心组件 #Pool中数据保存方式支持两种类型&…

智慧仓储物流可视化平台

通过图扑 HT 对仓储与物流流程进行动态可视化管理。实时跟踪库存状态和物流路径,优化资源配置与调度,提升运营效率及准确性,支持全流程决策优化及管理。

万字长文解读深度学习——卷积神经网络CNN

推荐阅读: 卷积神经网络(CNN)详细介绍及其原理详解 CNN笔记:通俗理解卷积神经网络 文章目录 🌺深度学习面试八股汇总🌺主要组件输入层卷积层 (Convolutional Layer)批归一化层(Batch Normalizat…

100+SCI科研绘图系列教程(R和python)

科研绘图系列:箱线图加百分比点图展示组间差异-CSDN博客科研绘图系列:箱线图加蜜蜂图展示组间数据分布-CSDN博客科研绘图系列:小提琴图和双侧小提琴图展示组间差异-CSDN博客科研绘图系列:组间差异的STAMP图的ggplot2实现-CSDN博客…

【在Linux世界中追寻伟大的One Piece】多路转接epoll

目录 1 -> I/O多路转接之poll 1.1 -> poll函数接口 1.2 -> poll的优点 1.3 -> poll的缺点 1.4 -> poll示例 1.4.1 -> 使用poll监控标准输入 2 -> I/O多路转接之epoll 2.1 -> 初识epoll 2.2 -> epoll的相关系统调用 2.2.1 -> epoll_cre…

招聘app开发,人才招聘、求职首要方式

随着互联网的快速发展,各种线上应用成为了大众生活中不可或缺的一部分,不管是购物、娱乐、找工作等都可以通过手机应用进行。因此,对于企业说,线上招聘app至关重要,能够快速帮助企业找到合适的人才。企业直接与求职者交…