go语言中的结构体详解
1.概念
在Go语言中,结构体是一种自定义的数据类型,用于将不同类型的字段组合在一起形成一个新的数据结构。结构体定义了一组字段,每个字段可以有不同的类型,这些字段一起构成了结构体的实例。通过结构体,我们可以将相关的数据进行组织和管理,从而更方便地进行操作和传递。
结构体的定义使用关键字type
和struct
。以下是结构体的定义语法:
type 结构体名称 struct {
字段1 类型1
字段2 类型2
// 更多字段...
}
1.1基本用法
type User struct {
Name string
Age int
Address string
}
首先,可以使用结构体字面量直接初始化结构体变量,按照字段顺序给出对应的值。示例如下:
person := Person{"Alice", 25, "广东深圳"}
其次可以使用指定字段名的方式给出字段的初始化值,这个时候可以忽略某些字段。示例如下:
person := Person{Name: "Alice", Age: 25}
也可以使用new关键字创建一个指向结构体的指针,并返回该指针。示例如下:
personPtr := new(Person)
personPtr.Name = "Alice"
personPtr.Age = 25
下面定义了结构体的基本用法:
package main
import "fmt"
// 定义结构体
type Person struct {
Name string
Age int
}
// 定义方法:打印个人信息
func (p Person) PrintInfo() {
fmt.Printf("Name: %s\n", p.Name)
fmt.Printf("Age: %d\n", p.Age)
}
// 定义方法:修改年龄
func (p Person) UpdateAge(newAge int) {
p.Age = newAge
}
func main() {
// 创建一个 Person 结构体实例
person := Person{Name: "John", Age: 30}
// 调用结构体的方法:打印个人信息
person.PrintInfo() // Output: Name: John Age: 30
// 调用结构体的方法:修改年龄
person.UpdateAge(35)
// 再次调用方法,打印修改后的个人信息
person.PrintInfo() // Output: Name: John Age: 35
}
运行结果:
2.结构体的可见性
结构体在Go语言中支持数据可见性的特性。其通过首字母大小写可以限制结构体字段和方法的可见性,从而实现信息的隐藏和封装。
在结构体中,方法名或者字段名,其首字母大写,代表着对外是可见和可修改的;首字母小写,则代表着对外为不可见的。如果想要读取或者修改,只能通过对外暴露的接口来进行,通过这种方式,可以隐藏结构体的内部实现细节,同时确保对结构体数据的访问和操作通过封装的公开方法进行,提供了更好的安全性和封装性。下面给出一个代码的示例:
package main
import "fmt"
// 定义结构体
type Person struct {
name string // 私有字段
}
// 定义公开方法:设置姓名
func (p *Person) SetName(name string) {
p.name = name
}
// 定义公开方法:获取姓名
func (p *Person) GetName() string {
return p.name
}
func main() {
// 创建一个 Person 结构体实例
person := Person{}
// 这里将无法通过编译
// person.name = "hello eva"
// 通过公开方法设置姓名和年龄
person.SetName("John")
// 通过公开方法获取姓名和年龄并打印
fmt.Println("Name:", person.GetName()) // Output: Name: John
}
• 首字母大写:如果结构体的字段名首字母是大写的(例如 Name),那么这个字段在包外也是可见的,即它是公开的(exported)。
• 首字母小写:如果结构体的字段名首字母是小写的(例如 name),那么这个字段只在定义它的包内可见,即它是非公开的(unexported)。
在这个例子中,Name 字段是公开的,可以在包外访问。而 age 字段是非公开的,只能在定义它的包内访问。
为了提供对非公开字段的控制访问,通常会在结构体上定义方法。这些方法可以是公开的,但它们操作的是私有字段。
package main
import "fmt"
type Person struct {
Name string // 公开字段
age int // 非公开字段
}
// 方法 SetAge 允许外部代码设置年龄,但保持 age 字段的私有性
func (p *Person) SetAge(a int) {
if a >= 0 {
p.age = a
}
}
// 方法 GetAge 允许外部代码获取年龄
func (p *Person) GetAge() int {
return p.age
}
func main() {
p:=Person{Name:"Alice"}
p.SetAge(30)
fmt.Println(p.Name,p.GetAge())
}
3 结构体能够实现接口
接口定义了一组方法的契约,描述了这些方法的行为和签名。
在Go语言中,接口是一种类型,由一组方法签名组成。一个结构体可以实现该接口中的所有方法,此时可以认为其实现了该接口,从而可以以相同的方式被使用。这种特性提供了多态性,允许我们编写通用的代码,适用于多种类型的结构体。以下是一个示例,演示了结构体如何实现一个接口:
步骤:
- 1. 定义接口:首先定义一个接口,指定需要实现的方法。
- 2. 定义结构体:定义一个结构体类型。
- 3. 实现方法:为结构体添加方法,方法的接收者是结构体的指针或值。
- 4. 接口赋值:可以将实现了接口的结构体赋值给接口类型的变量。
package main
import "fmt"
// 定义接口
type Shape interface {
Area() float64
Perimeter() float64
}
// 定义矩形结构体
type Rectangle struct {
Width float64
Height float64
}
// 实现接口方法:计算矩形的面积
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// 实现接口方法:计算矩形的周长
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
func main() {
// 创建一个矩形结构体实例
rectangle := Rectangle{Width: 5, Height: 3}
// 将矩形实例赋值给接口变量
var shape Shape
shape = rectangle
// 通过接口调用方法,计算面积和周长
area := shape.Area()
perimeter := shape.Perimeter()
fmt.Println("Area:", area) // Output: Area: 15
fmt.Println("Perimeter:", perimeter) // Output: Perimeter: 16
}
4.匿名字段
在Go语言中,匿名字段是一种特殊的字段,它允许一个结构体嵌入另一个结构体的字段,而不需要显式地命名这个嵌入的结构体。这样,嵌入的结构体的字段就可以直接通过外层结构体访问,就好像它们是外层结构体的直接字段一样。
匿名字段的作用
1. 代码复用:允许在不同的结构体中重用相同的字段。
2. 接口实现:通过嵌入接口,可以使一个结构体隐式地实现该接口。
3. 继承:虽然Go不支持传统的继承,但通过嵌入可以模拟继承的效果。
package main
import "fmt"
// 定义一个基本的个人信息结构体
type Person struct {
Name string
Age int
}
// 定义一个包含联系信息的结构体
type ContactInfo struct {
Email string
Phone string
}
// 定义一个Employee结构体,它嵌入了Person和ContactInfo
type Employee struct {
Person // 匿名字段,Person的字段将直接可用
ContactInfo // 另一个匿名字段
JobTitle string
}
func main() {
// 创建一个Employee实例
emp := Employee{
Person: Person{
Name: "Kimi",
Age: 30,
},
ContactInfo: ContactInfo{
Email: "kimi@moonshot.cn",
Phone: "1234567890",
},
JobTitle: "AI Assistant",
}
// 访问嵌入结构体的字段
fmt.Println("Employee Name:", emp.Name) // 输出: Employee Name: Kimi
fmt.Println("Employee Age:", emp.Age) // 输出: Employee Age: 30
fmt.Println("Employee Email:", emp.Email) // 输出: Employee Email: kimi@moonshot.cn
fmt.Println("Employee Phone:", emp.Phone) // 输出: Employee Phone: 1234567890
fmt.Println("Employee Job Title:", emp.JobTitle) // 输出: Employee Job Title: AI Assistant
}
Person 和 ContactInfo 是两个独立的结构体。
Employee 结构体通过匿名字段嵌入了 Person 和 ContactInfo。这意味着 Employee 的实例可以直接访问 Person 和 ContactInfo 的所有字段。
在创建 Employee 的实例时,可以直接初始化嵌入的 Person 和 ContactInfo 的字段。
这种使用匿名字段的方式使得代码更加简洁和易于管理,同时也提高了代码的复用性。
5.new函数
在Go语言中,结构体是一种自定义的数据类型,它允许你将多个不同类型的数据项组合成一个类型。结构体通常用于表示复杂的数据结构。
new 关键字在Go语言中用于分配内存。当你使用 new 来创建一个结构体实例时,它会分配足够的内存来存储结构体的字段,并将所有的字段初始化为该类型的零值。
下面是一个使用 new 关键字创建结构:
package main
import "fmt"
// 定义一个结构体类型
type Person struct {
Name string
Age int
}
func main() {
// 使用 new 分配内存
p := new(Person)
// 输出结构体的初始状态
fmt.Println("Before setting values:")
fmt.Printf("%+v\n", p)
// 设置结构体的字段
p.Name = "Alice"
p.Age = 30
// 输出结构体的当前状态
fmt.Println("After setting values:")
fmt.Printf("%+v\n", p)
}
在这个例子中,new(Person) 创建了一个 Person 类型的实例 p,并且所有的字段都被初始化为零值(对于字符串是空字符串,对于整数是0)。然后,你可以设置这些字段的值。
需要注意的是,new 关键字返回的是指针,指向新分配的内存。如果你需要一个值而不是指针,你可以使用 & 操作符来获取指针指向的值,或者 使用 var 关键字来直接创建一个值类型的实例:
p := Person{Name: "Alice", Age: 30} // 使用 var 创建,一个值类型的实例在这种情况下,p 是一个 Person 类型的值,而不是指针。
6.指针结构体
在Go语言中,指针和结构体是两个核心概念,它们经常一起使用,尤其是在处理大型数据结构时。使用指针可以提高程序的性能,尤其是在处理大型结构体时,因为指针允许你直接操作数据而不是数据的副本。
指针和结构体的基本概念
1. 指针:在Go中,指针是一个变量,它存储了另一个变量的内存地址。使用指针可以避免在函数调用时复制整个数据结构,从而节省内存和时间。
2. 结构体:结构体是Go中一种自定义的数据类型,它允许你将多个不同类型的数据项组合成一个单一的数据结构。
使用指针操作结构体
当你有一个结构体类型的变量时,你可以获取它的指针,然后通过这个指针来访问和修改结构体的字段。
创建结构体指针
你可以使用 new 或 & 操作符来创建一个结构体的指针:
个结构体的指针:
type Person struct {
Name string
Age int
}
func main() {
// 使用 new 创建指针
p := new(Person)
p.Name = "Alice"
p.Age = 30
// 使用 & 获取变量的地址
var person Person
personPointer := &person
personPointer.Name = "Bob"
personPointer.Age = 25
fmt.Println(*p) // 输出: {Alice 30}
fmt.Println(person) // 输出: {Bob 25}
}
在这个例子中,new(Person) 创建了一个 Person 类型的实例,并返回了指向这个实例的指针。&person 获取了 person 变量的地址,即它的指针。
通过指针访问和修改结构体字段
你可以通过指针访问和修改结构体的字段。使用指针访问字段时,使用 . 操作符:
(*p).Name = "Charlie" // 通过指针修改结构体字段
fmt.Println(p) // 输出: &{Charlie 30}
这里 (*p).Name 表示先对指针 p 解引用,然后访问 Name 字段。
使用指针结构体的好处包括:
1. 性能:传递指针比传递整个结构体更高效,尤其是在结构体很大时。
2. 直接修改:通过指针,你可以在函数内部直接修改原始数据,而不需要返回修改后的结构体。
3. 接口实现:在Go中,接口通常要求方法接收者是指针,以允许修改接口的实现。
原文地址:https://blog.csdn.net/2302_79993788/article/details/142342264
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!