当前位置: 首页 > news >正文

9.1go结构体

Go不是完全面向对象的,没有类的概念,所以结构体应该承担了更多的责任。

结构体定义

使用 type 和 struct 关键字定义:

type Person struct {

        Name string

        Age int

}

字段可以是任意类型,包括其他结构体或指针。

字段名以大写开头表示可导出(公开),小写表示私有(仅在包内可见)。

实例化

方式一:声明变量后赋值

        var p1 Person

        p1.Name = "Alice"

        p1.Age = 30

方式二:结构体字面量

        p2 := Person{Name: "Bob", Age: 25} // 指定字段名

        p3 := Person{"Charlie", 28} // 按字段顺序初始化(需注意顺序)

方式三:使用 new 函数(返回指针)

        p4 := new(Person)

        p4.Name = "Dave" // Go自动解引用,等价于 (*p4).Name

结构体的零值

当声明一个结构体变量而不初始化时,其字段会被赋予对应类型的零值。

var u Person
fmt.Println(u.Name) // 输出: ""
fmt.Println(u.Age)  // 输出: 0

结构体的构造函数

虽然 Go 不支持传统的构造函数,但可以通过定义函数来创建并初始化结构体实例,通常命名为 New【StructName】

func NewPerson(name string, age int) Person {
    return Person{
        Name: name,
        Age:  age,
    }
}

p := NewPerson("Laura", 32)
fmt.Println(p.Name) 

嵌套与匿名字段

结构体可以嵌套其他结构体,实现组合(类似继承):

type Employee struct {

        Person // 匿名字段(嵌入Person结构体)

        Position string

}

这样的话,Employee就嵌套了Person,访问Employee的Name可以直接用e.Name,而不需要e.Person.Name,这就是结构体嵌套匿名字段的作用。

// 访问嵌套字段

e := Employee{Person{"Eve", 40}, "Engineer"}

fmt.Println(e.Name) // 直接访问Person的字段(字段提升)

结构体方法

结构体可以定义方法,接收者为值或指针:

Go中的方法是在函数前面加上一个接收者。比如,给Person结构体定义一个方法:

这里要注意,如果使用值接收者,修改不会影响原结构体,而指针接收者会改变原结构体的值。

// 值接收者(操作副本)

// 指针接收者(操作原对象)

func main() {
	p := Person{
		Age:  29,
		Name: "Diana",
	}
	p.Birthday()
	fmt.Println(p.Age) // 输出: 29 (未改变)

	p.BirthdayPointer()
	fmt.Println(p.Age) // 输出: 30 (已改变)

}

type Person struct {
	Name string
	Age  int
}

// 值接收者
func (p Person) Birthday() {
	p.Age += 1
}

// 指针接收者
func (p *Person) BirthdayPointer() {
	p.Age += 1
}

继承

Employee 结构体嵌入了 Person结构体,因此 Employee 可以直接访问 Person的字段和方法

    e := Employee{Person{"Eve", 40}, "Engineer"}
	fmt.Println(e.Name, e.Age)
	e.Birthday()
	fmt.Println(e.Name, e.Age)
	e.BirthdayPointer()
	fmt.Println(e.Name, e.Age)

结构体作为参数

结构体作为参数传递给函数,如果是值传递,函数内部对结构体的修改不会影响原变量;如果是传递指针,则会修改原变量。

  • 值传递(拷贝副本)

直接将结构体作为值传递给函数,函数内部操作的是原结构体的副本,不会影响原对象。

示例:

package main

import "fmt"

type Point struct {

        X int

        Y int

}

// 值传递:修改副本,不影响原对象

func modifyValue(p Point) {

        p.X = 100

        fmt.Println("Inside modifyValue:", p) // 输出 {100 2}

}

func main() {

        p := Point{X: 1, Y: 2}

        modifyValue(p)

        fmt.Println("After modifyValue:", p) // 输出 {1 2}(原对象未改变)

}

适用场景:

结构体较小,拷贝成本低。

不需要修改原结构体的逻辑。

  • 指针传递(操作原对象)

传递结构体的指针(内存地址),函数内部操作的是原对象。

示例:

package main

import "fmt"

type Point struct {

        X int

        Y int

}

// 指针传递:修改原对象

func modifyPointer(p *Point) {

        p.X = 100

        fmt.Println("Inside modifyPointer:", *p) // 输出 {100 2}

}

func main() {

        p := &Point{X: 1, Y: 2}

        modifyPointer(p)

        fmt.Println("After modifyPointer:", p) // 输出 &{100 2}(原对象已改变)

}

适用场景:

结构体较大,避免拷贝开销。

需要修改原结构体的字段。

结构体字段覆盖

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

type Employee struct {
    Person // 嵌入Person结构体
    Name   string // 与Person中的Name字段同名
    Salary float64
}

func main() {
    e := Employee{
        Person: Person{Name: "Alice", Age: 30},
        Name:   "Eve",
        Salary: 70000,
    }

    fmt.Println(e.Name)          // 输出: Eve (外层Employee的Name字段)
    fmt.Println(e.Person.Name)   // 输出: Alice (内层Person的Name字段)
    fmt.Println(e.Age)           // 输出: 30
    fmt.Println(e.Salary)        // 输出: 70000
}
  • Employee 结构体嵌入了 Person 结构体,并且两者都有一个名为 Name 的字段。
  • 当访问 e.Name 时,默认访问的是 Employee 结构体的 Name 字段("Eve"),这遮蔽了内层 Person 的 Name 字段。
  • 要访问内层 Person 的 Name 字段,需要使用 e.Person.Name

结构体方法覆盖

package main

import "fmt"

type Person struct {
    Name string
}

func (p Person) Greet() {
    fmt.Printf("Hello, I'm %s from Person.
", p.Name)
}

type Employee struct {
    Person
    Name string
}

func (e Employee) Greet() {
    fmt.Printf("Hello, I'm %s from Employee.
", e.Name)
}

func main() {
    p := Person{Name: "Alice"}
    e := Employee{
        Person: Person{Name: "Bob"},
        Name:   "Eve",
    }

    p.Greet() // 输出: Hello, I'm Alice from Person.
    e.Greet() // 输出: Hello, I'm Eve from Employee.

    // 调用嵌入结构体的方法
    e.Person.Greet() // 输出: Hello, I'm Bob from Person.
}
  • Employee 结构体定义了自己的 Greet 方法,覆盖了嵌入的 Person 的 Greet 方法。
  • 调用 e.Greet() 会执行 Employee 的 Greet 方法。
  • 如果需要调用被遮蔽的 Person 的 Greet 方法,可以通过 e.Person.Greet() 显式调用。

结构体可见性

如果结构体的名称或字段的名称以大写字母开头,那么其他包可以访问;否则,只能在当前包内访问。比如:

type person struct { // 小写,只能在包内使用

          name string

          age int

}

type Student struct { // 大写,公开,包外可见

        Name string

        Age int

}

结构体其他特性

比较

若所有字段可比较(非切片、map等),结构体可直接用 == 或 != 比较。

包含不可比较字段的结构体无法直接比较。

深浅拷贝

默认赋值是值拷贝(深拷贝)。

若字段含引用类型(如切片、指针),拷贝后共享底层数据。

作为Map的键需所有字段可比较:

type Point struct { X, Y int }

points := make(map[Point]string)

points[Point{1,2}] = "start"

匿名字段冲突

若多个匿名字段有同名字段,需显式指定:

type A struct { Name string }

type B struct { Name string }

type C struct { A; B }

c := C{}

c.A.Name = "Alice" // 必须明确指定

相关文章:

  • 第9章 管理日志(网络安全防御实战--蓝军武器库)
  • Linux13-TCP\HTTP
  • 条码扫描手持pda,推动服装零售门店管理效率
  • 大模型开发(四):PET项目——新零售决策评价系统(上)
  • 大白话CSS 优先级计算规则的详细推导与示例
  • HarmonyOS NEXT开发实战:DevEco Studio中DeepSeek的使用
  • uniapp小程序对接腾讯IM即时通讯无ui集成(1)
  • 手机屏幕摔不显示了,如何用其他屏幕临时显示,用来导出资料或者清理手机
  • 77.ObservableCollection使用介绍1 C#例子 WPF例子
  • golang从入门到做牛马:第六篇-Go语言变量存储数据的“小盒子”
  • ubuntu22.04安装RAGFlow配合DeepSeek搭建本地知识库
  • C# ArrayPool
  • LeetCode 102. 二叉树的层序遍历,BFS(广度优先搜索)(Python)
  • CCF-CSP第29次认证第一题 --《田地丈量》
  • C#使用winform实现简单的梯形图指令编译和执行,带编译器和虚拟机代码
  • 浏览器安全问题
  • Go语言集成DeepSeek API和GoFly框架文本编辑器实现流式输出和对话(GoFly快速开发框架)
  • 【maven】maven依赖报错解决方式
  • 数据分析/数据科学常见SQL题目:连续登录用户、留存率、最大观看人数
  • typora高亮方案+鼠标侧键一键改色
  • 怎么做卖辅助网站/电脑培训机构
  • 个人网站网页首页/网站建设合同模板
  • 周浦高端网站建设公司/长春seo顾问
  • 百度广州分公司/网站seo哪家做的好
  • 荆门网站建设服务/企业网站推广方案设计毕业设计
  • 用织梦网站后台发布文章为什么还需要审核/天津关键词排名提升