Go类型专题总结

1、为什么在常见的代码里,接口变量var test someInterface一般被赋予指针值,而非对象?

首先假设定义一个接口

type BaseType interface {
	Opt(a int, b int) string
}

我们常见的实现方式时在指针上实现这个接口

type PType struct {
	Name string
}

func (thiz *PType) Opt(a int, b int) string {
	fmt.Printf("PType函数内对象地址-->%p\n", thiz)
	return strconv.Itoa(a + b)
}

常见的使用方式是

var base_p interfaceType.BaseType = new(interfaceType.PType)

那么,

1、Go里面对接口类型变量(如var base_p interfaceType.BaseType)只能被赋予指针值(如new(interfaceType.PType))吗?

2、Go里面接口只能在指针上实现(如func (thiz *PType) Opt(a int, b int) string)吗?

两个问题的答案都是否定的。看看下面的例子:

package interfaceType

import (
	"fmt"
	"strconv"
)

type BaseType interface {
	Opt(a int, b int) string
}

type PType struct {
	Name string
}

func (thiz *PType) Opt(a int, b int) string {
	fmt.Printf("PType函数内对象地址-->%p\n", thiz)
	return strconv.Itoa(a + b)
}

type OType struct {
	Id string
}

func (this OType) Opt(a int, b int) string {
	fmt.Printf("OType函数内对象地址-->%p\n", &this)
	return strconv.Itoa(b - a)
}
package main

import "fmt"
import "./interfaceType"

func main() {
	var base_p interfaceType.BaseType = new(interfaceType.PType)
	fmt.Printf("PType对象地址-->%p\n", base_p)
	fmt.Println(base_p.Opt(1, 2))
	var base_o interfaceType.BaseType = interfaceType.OType{}
	fmt.Printf("OType对象地址-->%p\n", &base_o)
	fmt.Println(base_o.Opt(3, 4))

}

运行结果:

PType对象地址-->0xc000010200
PType函数内对象地址-->0xc000010200
3
OType对象地址-->0xc000010220
OType函数内对象地址-->0xc000010240
1

这说明:接口变量可以被赋予指针值,也可以被赋予对象;到底该赋予那种值,取决于接口是在指针还是在结构上实现的。

之所以通常在指针上实现是因为:Go是彻底的值传递的(与C/C++一致)。分解下上面的两种实现指针的方式:

指针实现:

首先看 base_p.Opt(1, 2)

此时Opt函数是在base_p上调用的。为什么不能使用(*base_p).Opt(1,2)呢?

因为(*base_p)得到的是一个type PType struct,而在type PType struct上并没有实现Opt(a int, b int) string,所以编译器会报

invalid indirect of base_p (type interfaceType.BaseType)

错误。

base_p.Opt(1, 2)对应的函数是

func (thiz *PType) Opt(a int, b int) string {
    fmt.Printf("PType函数内对象地址-->%p\n", thiz)
    return strconv.Itoa(a + b)
}

很显然,这个函数在执行之前会发生形参赋值

thiz = base_p

a = 1

b = 2

这时,打印语句fmt.Printf("PType函数内对象地址-->%p\n", thiz)

打印的是thiz的值,即thiz指向的对象的值,显然就是base_p指向的对象的值,也就是new(interfaceType.PType)

所以,两次打印语句

fmt.Printf("PType对象地址-->%p\n", base_p)

fmt.Printf("PType函数内对象地址-->%p\n", thiz)

打印的值是一致的!

同理,分析直接在类型上实现:

base_o.Opt(3, 4)对应的函数是

func (this OType) Opt(a int, b int) string {
    fmt.Printf("OType函数内对象地址-->%p\n", &this)
    return strconv.Itoa(b - a)
}

同样在函数执行之前会发生形参赋值

this = base_o

a = 3

b =4

很显然,由于Go是彻底的值传递,所以在执行this = base_o时,已经发生了拷贝赋值。所以两次打印语句

fmt.Printf("OType对象地址-->%p\n", &base_o)

fmt.Printf("OType函数内对象地址-->%p\n", &this)

打印的结果是不一样的。

所以综上,直接在类型上实现接口有两点坏处:

1、发生对象的拷贝赋值,浪费内存(但是这还不是最严重的问题)

2、如果在所实现的接口函数内有涉及对象成员参数的操作,这些操作将对调用接口函数的对象无效(这一点在C++里面不存在)!

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值