Golang中如何将C++基类方法转换为Go语言实现

Golang中如何将C++基类方法转换为Go语言实现 我是一名经验丰富的C++程序员,但对Go语言完全陌生。我正在尝试学习Go,以决定它是否适合我。也许我的问题在于我仍在“用C++的思维思考”。

我创建了我的第一个应用程序。它是一个虚拟的图形程序。它实现了四种形状(圆形、正方形等),每种形状都有一个draw()方法——完全是虚拟的——draw()只是输出一条消息“在某个地址绘制一个正方形”。

我有一个名为drawableshape的接口,其中包含draw()方法,所以我所有的形状都是可绘制的形状。

在C++中,我会让所有这些形状都基于一个包含公共成员(变量和方法)的基类。在我的Go程序中,我有一个shapebase结构体,并将其嵌入到每个形状中。它有一个变量,一个float64类型的position。(我的绘图画布是一维的)。

我想实现一个move(increment float64)函数。它对每个形状都是相同的——将增量加到位置上。在C++中,我会把这个方法放在基类中,然后每个形状都会继承这个单一的move()方法。显然,在Go中我不能完全这样做。我应该怎么做呢?我能想到几种解决方案,但它们似乎都过于冗长或复杂。

  • 为每个形状创建一个完全相同的move()方法,该方法要么直接增加shapebase的position,要么调用shapebase上的move()方法。这看起来有很多重复的代码。
  • 在每个形状中创建一个getbase()方法,返回其shapebase结构体的地址。然后我可以做类似mysquare.getbase().move(increment)这样的事情。这似乎过于复杂了,至少在目前shapebase只有一个方法的情况下是这样。

在Go中我应该怎么做这件事?我寻找的是处理这类情况的“正确方法”,而不仅仅是这个简单编程问题的答案。


更多关于Golang中如何将C++基类方法转换为Go语言实现的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

这是您要找的吗:https://play.golang.org/p/z8kJ50K11lr

更多关于Golang中如何将C++基类方法转换为Go语言实现的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


是的!完美!谢谢。我花了一些时间才理解你的 Circle 是如何实现 Move() 的。我一直在通过 Golang.orgyourbasic.org/Golang 上的教程学习 Go,我认为这两个教程都没有提到可以将一个结构体“嵌入”到另一个结构体中,并且其成员实际上会成为外部或包含结构体的成员。这——按照我 C++ 的思维方式——使得内部结构体“工作起来像”一个父类。我需要实践一下并思考一下。再次感谢。

为了澄清,我推断我可以在 Circle 结构体中嵌入一个名为 s、类型为 shapebase 的变量 s shapebase,但没想到我可以直接编写 shapebase 就将 shapebase“合并”到 Circle 中。

在Go语言中处理共享方法的最佳方式是使用组合和接口。对于你的场景,正确的做法是让shapebase实现move()方法,然后通过嵌入来复用这个方法。以下是具体实现:

// 定义可绘制接口
type DrawableShape interface {
    Draw()
}

// 基础形状结构体
type ShapeBase struct {
    Position float64
}

// 基础移动方法
func (s *ShapeBase) Move(increment float64) {
    s.Position += increment
}

// 圆形
type Circle struct {
    ShapeBase // 嵌入基础结构体
    Radius    float64
}

func (c *Circle) Draw() {
    fmt.Printf("在位置 %.2f 绘制半径为 %.2f 的圆形\n", c.Position, c.Radius)
}

// 正方形
type Square struct {
    ShapeBase // 嵌入基础结构体
    Side      float64
}

func (s *Square) Draw() {
    fmt.Printf("在位置 %.2f 绘制边长为 %.2f 的正方形\n", s.Position, s.Side)
}

// 使用示例
func main() {
    shapes := []DrawableShape{
        &Circle{ShapeBase: ShapeBase{Position: 0}, Radius: 5},
        &Square{ShapeBase: ShapeBase{Position: 10}, Side: 4},
    }
    
    // 移动所有形状
    for _, shape := range shapes {
        // 类型断言调用Move方法
        if c, ok := shape.(interface{ Move(float64) }); ok {
            c.Move(3.5)
        }
        shape.Draw()
    }
}

更优雅的解决方案是创建组合接口:

// 可移动接口
type Movable interface {
    Move(float64)
}

// 完整形状接口
type Shape interface {
    DrawableShape
    Movable
}

// 修改基础结构体,使其实现Movable接口
func (s *ShapeBase) Move(increment float64) {
    s.Position += increment
}

// 现在可以这样使用
func processShapes(shapes []Shape) {
    for _, shape := range shapes {
        shape.Move(2.0)  // 直接调用,无需类型断言
        shape.Draw()
    }
}

对于需要访问基础结构体的情况,可以直接通过嵌入访问:

func main() {
    circle := &Circle{
        ShapeBase: ShapeBase{Position: 0},
        Radius:    5,
    }
    
    // 直接调用嵌入结构体的方法
    circle.Move(2.5)
    fmt.Printf("新位置: %.2f\n", circle.Position) // 访问嵌入字段
    
    // 或者通过结构体名访问
    circle.ShapeBase.Move(1.5)
}

Go语言的嵌入机制允许你复用方法而不需要重复代码。嵌入结构体的方法会被提升到外层结构体,所以你可以直接调用circle.Move(),就像这个方法是在Circle上定义的一样。这种方式既避免了代码重复,又保持了类型安全。

回到顶部