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语言的嵌入既保持了类型安全,又提供了灵活的组合方式,同时避免了传统继承的复杂性。

回到顶部