文档章节

Go方法——基于对象指针的方法

秋风醉了
 秋风醉了
发布于 2016/07/10 11:36
字数 1091
阅读 89
收藏 1

Go方法——基于对象指针的方法

当调用一个函数时,会对其每一个参数值进行拷贝,如果一个函数需要更新一个变量,或者函数的其中一个参数实在太大我们希望能够避免进行这种默认的拷贝,这种情况下我们就需要用到指针了。对应到我们这里用来更新接收器的对象的方法,当这个接受者变量本身比较大时,我们就可以用其指针而不是对象来声明方法,如下:

package main

import (
	"fmt"
	"math"
)

func main() {

	// 三种调用方式
	r := &Point{1, 2} // 指针类型 r
	r.ScaleBy(2)      // 调用指针类型的方法
	fmt.Println(*r)   // "{2, 4}"

	p := Point{1, 2}
	pptr := &p      // 取地址符
	pptr.ScaleBy(2) // 通过指针调用方法
	fmt.Println(p)  // "{2, 4}"

	p := Point{1, 2}
	(&p).ScaleBy(2) // &p 获取 p的指针地址
	fmt.Println(p)  // "{2, 4}"

}

type Point struct{ X, Y float64 }

// traditional function
func Distance(p, q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// same thing, but as a method of the Point type
func (p Point) Distance(q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// 指针对象的方法,需要类型 Point 的指针调用该方法
// 这个方法的名字是(*Point).ScaleBy。这里的括号是必须的;没有括号的话这个表达式可能会被理解为*(Point.ScaleBy)。
func (p *Point) ScaleBy(factor float64) {
	p.X *= factor
	p.Y *= factor
}

上面代码演示了三种调用方式,如下,

// 三种调用方式
r := &Point{1, 2} // 指针类型 r
r.ScaleBy(2)      // 调用指针类型的方法
fmt.Println(*r)   // "{2, 4}"

p := Point{1, 2}
pptr := &p      // 取地址符
pptr.ScaleBy(2) // 通过指针调用方法
fmt.Println(p)  // "{2, 4}"

p := Point{1, 2}
(&p).ScaleBy(2) // &p 获取 p的指针地址
fmt.Println(p)  // "{2, 4}"

其实总结一点就是通过指针调用基于指针的方法。

但Go帮我们做了更多,帮我们简化了调用方式,如下示例,

package main

import (
	"fmt"
	"math"
)

func main() {
	// 更简便的调用方式
	pp := Point{4, 6} //pp 是Point 类型的变量
	pp.ScaleBy(2)     //直接用 Point 类型变量 pp 调用基于指针的方法也是可行的,是因为 Go 会隐式转换,自动转换为(&pp).ScaleBy(2)
	fmt.Println(pp)   // "{2, 4}"
}

type Point struct{ X, Y float64 }

// traditional function
func Distance(p, q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// same thing, but as a method of the Point type
func (p Point) Distance(q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// 指针对象的方法,需要类型 Point 的指针调用该方法
// 这个方法的名字是(*Point).ScaleBy。这里的括号是必须的;没有括号的话这个表达式可能会被理解为*(Point.ScaleBy)。
func (p *Point) ScaleBy(factor float64) {
	p.X *= factor
	p.Y *= factor
}

上面这段代码演示了使用 Point 类型的变量调用基于指针的方法时Go发生的隐式转换,其实使用 Point 类型的指针变量调用基于类型的方法Go也会自动隐式转换,如下,

package main

import (
	"fmt"
	"math"
)

func main() {
	pptr0 := &Point{1, 2}     //pptr0 指针类型
	p6 := Point{4, 6}         // Point 类型
	res := pptr0.Distance(p6) // 这里Go会发生隐式转换 自动转换为 (*pptr0).Distance(p6)
	fmt.Println(res)

}

type Point struct{ X, Y float64 }

// traditional function
func Distance(p, q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// same thing, but as a method of the Point type
func (p Point) Distance(q Point) float64 {
	return math.Hypot(q.X-p.X, q.Y-p.Y)
}

// 指针对象的方法,需要类型 Point 的指针调用该方法
// 这个方法的名字是(*Point).ScaleBy。这里的括号是必须的;没有括号的话这个表达式可能会被理解为*(Point.ScaleBy)。
func (p *Point) ScaleBy(factor float64) {
	p.X *= factor
	p.Y *= factor
}

总结:

  1. 不管你的method的receiver是指针类型还是非指针类型,都是可以通过指针/非指针类型进行调用的,编译器会帮你做类型转换(解引用和取地址)。
  2. 在声明一个method的receiver该是指针还是非指针类型时,你需要考虑两方面的内部,第一方面是这个对象本身是不是特别大,如果声明为非指针变量时,调用会产生一次拷贝;第二方面是如果你用指针类型作为receiver,那么你一定要注意,这种指针类型指向的始终是一块内存地址,就算你对其进行了拷贝。熟悉C或者C艹的人这里应该很快能明白。

=========END=========

© 著作权归作者所有

共有 人打赏支持
秋风醉了
粉丝 232
博文 574
码字总数 405033
作品 0
朝阳
程序员
虚拟机对象

1.虚拟机对象的创建 虚拟机遇到new指令 (1) 类加载: 检查该指令参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载,解析和初始化过。如果没有,必须先...

Turnsole1
01/07
0
0
Golang 学习笔记(09)—— json和xml解析

本文为转载,原文:Golang 学习笔记(09)—— json和xml解析 Golang xml package 函数 Marshal Marshal函数返回v的XML编码。 Marshal处理数组或者切片时会序列化每一个元素。Marshal处理指针...

ChainZhang
01/03
0
0
JavaScript面向对象编程实践

面向对象的几个概念 在进入正题前,先了解传统的面向对象编程(例如Java)中常会涉及到的概念,大致可以包括: 类:定义对象的特征。它是对象的属性和方法的模板定义。 对象(或称实例):类...

-波仔-
2016/03/22
11
0
C++ 备忘录 ( 持续更新中... )

Tip 1: #include包含头文件的问题 新的编译器格式: #include <iostream>using namespace std; 旧的编译器格式: #include <iostream.h> 注:旧的编译器格式中,包含头文件后,不添加 “usi...

谷鹏
2011/04/12
0
1
OC常量、变量、属性及其特性

1.Objective-C中声明常量使用关键字const。如:const double PI = 3.1514; 2.Objective-C中变量可以分为成员变量、局部变量和全局变量(用的很少,尽量不要用)。 //————————---------...

hejunbinlan
2016/05/16
77
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

OSChina 周日乱弹 —— 种族不同,禁止交往

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @小小编辑:推荐歌曲《苏菲小姐》- 鱼果 《苏菲小姐》- 鱼果 手机党少年们想听歌,请使劲儿戳(这里) @貓夏:下大雨 正是睡觉的好时候 临睡前...

小小编辑
今天
226
6
Python 搭建简单服务器

Python动态服务器网页(需要使用WSGI接口),基本实现步骤如下: 1.等待客户端的链接,服务器会收到一个http协议的请求数据报 2.利用正则表达式对这个请求数据报进行解析(请求方式、提取出文...

代码打碟手
今天
1
0
Confluence 6 删除垃圾内容

属性(profile)垃圾 属性垃圾的定义为,一个垃圾用户在 Confluence 创建了用户,但是这个用户在自己的属性页面中添加了垃圾 URL。 如果你有很多垃圾用户在你的系统中创建了属性,你可以使用...

honeymose
今天
1
0
qduoj~前端~二次开发~打包docker镜像并上传到阿里云容器镜像仓库

上一篇文章https://my.oschina.net/finchxu/blog/1930017记录了怎么在本地修改前端,现在我要把我的修改添加到部署到本地的前端的docker容器中,然后打包这个容器成为一个本地镜像,然后把这...

虚拟世界的懒猫
今天
1
0
UML中 的各种符号含义

Class Notation A class notation consists of three parts: Class Name The name of the class appears in the first partition. Class Attributes Attributes are shown in the second par......

hutaishi
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部