Golang中嵌入机制的内部实现解析
Golang中嵌入机制的内部实现解析
import "fmt"
//---------------------
type sun struct{
i1 int
i2 int
}
func (s sun)one(){
fmt.Println(s)
}
func (s *sun)two(){
fmt.Println(*s)
s.i2= 4321
}
//-------------------------
type dal struct{
inter
}
//-------------------------
type inter interface {
one()
two()
}
//---------------------------
func main(){
var z sun = sun{1,2}
var x dal = dal{&z}
x.one()
x.two()
x.two()
x.one()
fmt.Println(x.one)
fmt.Println(x.inter.one)
fmt.Println(z.one)
}
更多关于Golang中嵌入机制的内部实现解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang中嵌入机制的内部实现解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中,嵌入机制通过结构体字段的内存布局和接口表来实现。以下是针对您代码的内部实现解析:
1. 接口嵌入的内存布局
当结构体嵌入接口类型时,实际上存储的是接口值(包含类型指针和数据指针):
type dal struct {
inter // 接口字段,占用两个字(64位系统:16字节)
}
// 实际内存布局相当于:
type dal struct {
interType unsafe.Pointer // 指向接口类型信息
interData unsafe.Pointer // 指向实际数据
}
2. 方法调用的内部转换
当调用嵌入接口的方法时,编译器会自动转发调用:
// x.one() 在编译时转换为:
func (d dal) one() {
d.inter.one() // 直接调用接口的方法
}
// 实际生成的汇编代码类似于:
// MOVQ d.interType, AX // 加载接口类型
// MOVQ d.interData, BX // 加载接口数据
// CALL ·sun.one(SB) // 调用具体方法
3. 值接收者 vs 指针接收者
您的代码展示了关键区别:
func (s sun) one() { // 值接收者:复制整个结构体
fmt.Println(s) // 打印副本
}
func (s *sun) two() { // 指针接收者:传递指针
fmt.Println(*s) // 解引用打印
s.i2 = 4321 // 修改原始数据
}
4. 接口方法集规则
// sun 类型的方法集:
// - one() (值接收者)
// - two() (指针接收者,但可通过值调用)
// *sun 类型的方法集:
// - one() (值接收者,可通过指针调用)
// - two() (指针接收者)
// 因此:
var z sun = sun{1, 2}
var x dal = dal{&z} // 必须传递指针,因为 two() 是指针接收者
5. 方法表达式分析
fmt.Println(x.one) // 输出方法地址(dal.one 的包装器)
fmt.Println(x.inter.one) // 输出接口方法地址(sun.one)
fmt.Println(z.one) // 输出具体类型方法地址(sun.one)
// 这三个地址不同:
// 1. x.one: dal.one 包装函数地址
// 2. x.inter.one: 接口方法表指针
// 3. z.one: sun.one 实际函数地址
6. 完整的内部实现示例
package main
import (
"fmt"
"unsafe"
)
// 模拟接口内部结构
type iface struct {
tab unsafe.Pointer // 类型信息
data unsafe.Pointer // 数据指针
}
type sun struct {
i1 int
i2 int
}
func (s sun) one() {
fmt.Printf("one: i1=%d, i2=%d\n", s.i1, s.i2)
}
func (s *sun) two() {
fmt.Printf("two: i1=%d, i2=%d\n", s.i1, s.i2)
s.i2 = 4321
}
type inter interface {
one()
two()
}
type dal struct {
inter
}
func main() {
z := sun{1, 2}
x := dal{&z}
// 查看内部结构
iface := (*iface)(unsafe.Pointer(&x.inter))
fmt.Printf("接口类型指针: %p\n", iface.tab)
fmt.Printf("接口数据指针: %p\n", iface.data)
fmt.Printf("原始变量地址: %p\n", &z)
// 方法调用
x.one() // 输出: one: i1=1, i2=2
x.two() // 输出: two: i1=1, i2=2,修改 i2=4321
x.one() // 输出: one: i1=1, i2=4321
}
7. 编译器生成的包装方法
对于嵌入接口的方法调用,编译器会生成包装方法:
// 编译器为 dal 生成的方法包装器
func (d dal) one() {
// 通过接口调用实际方法
d.inter.one()
}
func (d dal) two() {
d.inter.two()
}
这种实现机制使得Go语言的嵌入既保持了类型安全,又提供了灵活的组合方式,同时避免了传统继承的复杂性。

