go 指针接收者和值接收者的区别小结

2025-04-17 17:50

本文主要是介绍go 指针接收者和值接收者的区别小结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《go指针接收者和值接收者的区别小结》在Go语言中,值接收者和指针接收者是方法定义中的两种接收者类型,本文主要介绍了go指针接收者和值接收者的区别小结,文中通过示例代码介绍的非常详细,需要的朋友们下...

go 指针接收者和值接收者的区别

指针接收者和值接收者的区别主要有两点:

  • Go 中函数传参是传值,因此指针接收者传递的是接收者的指针拷贝,值接收android者传递的是接收者的拷贝---在方法中指针接收者的变量会被修改,而值接收者的成员变量修改是无效的(毕竟传入的是拷贝,修改的自然也是拷贝的成员变量)。
  • 在接口实现上,如果值接收者实现了某个方法,相当于值接收者和指针接收者都实现了这个方法。反之则不行,即指针接收者实现了某个方法,不能看成值接收者实现这个方法。

注意第2点只影响接口实现,如果不涉及接口的话是不受到这条规则约束的。

一个助记的但是不太正确的观点:接收者是指针类型的方法,很可能在方法中会对接收者的属性进行更改操作,从而影响接收者;而对于接收者是值类型的方法,在方法中不会对接收者本身产生影响。

理解角度:指针接收者可以选择修改自己的值和不修改,值接收者修改自己的值是无效的(因为是拷贝)。所以可以看成指针接收者的能力>值接收者的能力,自然:指针接收者实现某个方法可以看成值接收者实现某个方法,反之则不行。

package main

import "fmt"

type coder interface {
    code()
    debug()
}

type Gopher struct {
    language string
}

func (p Gopher) code() {
    fmt.Printf("I am coding %s language\n", p.language)
}

func (p *Gopher) debug() {
    fmt.Printf("I am debuging %s language\n", p.lanhttp://www.chinasem.cnguage)
}

func main() {
    var c coder = &Gopher{"Go"} //运行正常
    //上一行换成: var c coder = Gopher{"Go"} 
    // 则报错 Gopher does not implement coder (debug method has pointer receiver)
    c.code()
    c.debug()
}

注意:对于上面的第 2 点补充:虽然隐式实现的方法不一样,但是如果不涉及接口,单纯在调用的时候,无论是指针接收者还是值接收者实现了某个方法,指针接收者和值接收者都可以调用。本质上是 go 的语法糖。

最佳实践:

如果类型具备“原始的本质”,即其成员都是由 Go 语言里内置的原始类型,如字符串,整型值等,那就定义值接收者类型的方法。

内置的引用类型,如 slice,map,interface,channel,这些类型比较特殊,声明他们的时候,实际上是创建了一个 header​, 对于他们也是直接定义值接收者类型的方法。这样,调用函数时,是直接 copy 了这些类型的 header​,而 header​ 本身就是为复制设计的。

如果类型具备非原始的本质,不能被安全地复制,这种类型总是应该被共享,那就定义指针接收者的方法。

虽然上面是这么说,但是个人感觉在实际使用中,基本可以无脑使用指针接收者。主要原因在于:1.指针接收者没有值拷贝带来的巨大开销。2.如果就想在函数中修改值就必须使用指针接收者。3.值接收者唯一的优势就是为了防止意外的修改,为了防止这一点可以通过创建一个函数或者方法来手动 copy,而这并不会带来多大的开销。

易错点辨析

是否改变结构体的值看的是方法是指针接收者还是值接收者,而不是看调用方是指针还是值。原因在于golang编译器在背后会完成一些工作,比如:解引用,隐式使用引用。代码范例如下:

package main

import "fmt"

type Node struct {
    val int
}

func (receiver Node) changeVal1() {
    receiver.val++
}

func (receiverChina编程 *Node) changeVal2() {
    receiver.val++
}
func main() {
    someOne := Node{}
    someOne.changeVal1()
    fmt.Printf("%d\n", someOne.v编程al) //0 ,说明没有改值,因为方法接收者是值接收者

    someOnePtr := &Node{}
    someOnePtr.changeVal1()
    fmt.Printf("%d\n", someOnePtr.jsval) //0 ,说明没有改值,因为方法接收者是值接收者,与调用方是否指针无关

    someTwo := Node{}
    someTwo.changeVal2()
    fmt.Printf("%d\n", someTwo.val) //1 ,说明成功改值,因为方法接收者是指针接收者,与调用方是否指针无关

    someTwoPtr := &Node{}
    someTwoPtr.changeVal2()
    fmt.Printf("%d\n", someTwoPtr.val) //1 ,说明成功改值,因为方法接收者是指针接收者,与调用方是否指针无关

}

参考:https://golang.design/go-questions/interface/receiver/

到此这篇关于go 指针接收者和值接收者的区别小结的文章就介绍到这了,更多相关go 指针接收者和值接收者内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于go 指针接收者和值接收者的区别小结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154262

相关文章

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

Python中的getopt模块用法小结

《Python中的getopt模块用法小结》getopt.getopt()函数是Python中用于解析命令行参数的标准库函数,该函数可以从命令行中提取选项和参数,并对它们进行处理,本文详细介绍了Pyt... 目录getopt模块介绍getopt.getopt函数的介绍getopt模块的常用用法getopt模

C 语言中enum枚举的定义和使用小结

《C语言中enum枚举的定义和使用小结》在C语言里,enum(枚举)是一种用户自定义的数据类型,它能够让你创建一组具名的整数常量,下面我会从定义、使用、特性等方面详细介绍enum,感兴趣的朋友一起看... 目录1、引言2、基本定义3、定义枚举变量4、自定义枚举常量的值5、枚举与switch语句结合使用6、枚

Java中的Lambda表达式及其应用小结

《Java中的Lambda表达式及其应用小结》Java中的Lambda表达式是一项极具创新性的特性,它使得Java代码更加简洁和高效,尤其是在集合操作和并行处理方面,:本文主要介绍Java中的La... 目录前言1. 什么是Lambda表达式?2. Lambda表达式的基本语法例子1:最简单的Lambda表

Java中Scanner的用法示例小结

《Java中Scanner的用法示例小结》有时候我们在编写代码的时候可能会使用输入和输出,那Java也有自己的输入和输出,今天我们来探究一下,对JavaScanner用法相关知识感兴趣的朋友一起看看吧... 目录前言一 输出二 输入Scanner的使用多组输入三 综合练习:猜数字游戏猜数字前言有时候我们在

SQL BETWEEN 的常见用法小结

《SQLBETWEEN的常见用法小结》BETWEEN操作符是SQL中非常有用的工具,它允许你快速选取某个范围内的值,本文给大家介绍SQLBETWEEN的常见用法,感兴趣的朋友一起看看吧... 在SQL中,BETWEEN是一个操作符,用于选取介于两个值之间的数据。它包含这两个边界值。BETWEEN操作符常用

售价599元起! 华为路由器X1/Pro发布 配置与区别一览

《售价599元起!华为路由器X1/Pro发布配置与区别一览》华为路由器X1/Pro发布,有朋友留言问华为路由X1和X1Pro怎么选择,关于这个问题,本期图文将对这二款路由器做了期参数对比,大家看... 华为路由 X1 系列已经正式发布并开启预售,将在 4 月 25 日 10:08 正式开售,两款产品分别为华

python uv包管理小结

《pythonuv包管理小结》uv是一个高性能的Python包管理工具,它不仅能够高效地处理包管理和依赖解析,还提供了对Python版本管理的支持,本文主要介绍了pythonuv包管理小结,具有一... 目录安装 uv使用 uv 管理 python 版本安装指定版本的 Python查看已安装的 Python

C#中DrawCurve的用法小结

《C#中DrawCurve的用法小结》本文主要介绍了C#中DrawCurve的用法小结,通常用于绘制一条平滑的曲线通过一系列给定的点,具有一定的参考价值,感兴趣的可以了解一下... 目录1. 如何使用 DrawCurve 方法(不带弯曲程度)2. 如何使用 DrawCurve 方法(带弯曲程度)3.使用Dr