【问题标题】:When is it a good idea to return a pointer to a struct?什么时候返回指向结构的指针是个好主意?
【发布时间】:2012-06-11 20:13:50
【问题描述】:

我正在学习 Go,但对于何时使用指针有点困惑。具体来说,从函数返回struct时,什么时候返回struct实例本身合适,什么时候返回struct的指针合适?

示例代码:

type Car struct {
  make string
  model string
}

func Whatever() {
  var car Car

  car := Car{"honda", "civic"}

  // ...

  return car
}

在哪些情况下我想要返回指针,而我不想想要返回指针?有没有好的经验法则?

【问题讨论】:

  • 同样的规则不适用吗?
  • 不,不同语言的不同规则。每种语言都有它的警告,我个人不知道 Go,所以我不能代表它,但我知道在 C 中,返回指向分配在堆栈上的对象的指针是一个巨大的禁忌。
  • 我对答案感到不安:我认为灰色区域与小物体的性能有关,我认为除了分析之外无法估计最快的解决方案。传递小于 64 位的值对象是否会更快?

标签: pointers go


【解决方案1】:

您需要牢记两点,性能和 API。

如何使用汽车?它是一个有状态的对象吗?它是一个大结构吗?不幸的是,当我不知道汽车是什么时,我无法回答。说实话,最好的方法是看看别人做了什么并复制他们。最终,你会对这种事情产生一种感觉。我现在将描述标准库中的三个示例,并解释为什么我认为他们使用了他们所做的。

  1. hash/crc32crc32.NewIEEE()函数返回一个指针类型(实际上是一个接口,但底层类型是一个指针)。散列函数的实例具有状态。当您将信息写入哈希时,它会汇总数据,因此当您调用 Sum() 方法时,它将为您提供该实例的状态。

  2. timetime.Date 函数返回一个Time 结构。为什么?时间就是时间。它没有状态。它就像一个整数,您可以在其中比较它们、对它们进行数学运算等。API 设计者决定对时间的修改不会改变当前的时间,而是创建一个新的时间。作为图书馆的用户,如果我想要一个月后的时间,我会想要一个新的时间对象,而不是更改我拥有的当前对象。一次也只有3个字长。换句话说,它很小,使用指针不会有性能提升。

  3. math/big: big.NewInt() 是一个有趣的。我们几乎同意,当您修改 big.Int 时,您通常会想要一个新的。 big.Int 没有内部状态,为什么是指针呢?答案就是性能。程序员意识到大整数是……大。每次进行数学运算时不断分配可能不切实际。因此,他们决定使用指针,让程序员决定何时分配新空间。

我回答你的问题了吗?可能不是。这是一个设计决策,您需要根据具体情况进行计算。当我设计自己的库时,我使用标准库作为指南。这实际上都归结为判断以及您希望客户端代码如何使用您的类型。

【讨论】:

  • 我不得不读了几遍才意识到这是一个非常好的答案。谢谢。
  • 很好的答案。我是 Go 新手,我是 IRC 上#go-nuts 的 Stephen 推荐的。
【解决方案2】:

很迷茫,在特定情况下很可能会出现异常:

  • 返回一个非常小的值(不超过几个字)。
  • 当复制开销会严重影响性能(大小是很多字)时,返回一个指针。

【讨论】:

    【解决方案3】:

    通常,当你想模仿面向对象的风格时,你有一个存储状态的“对象”和一个可以改变对象的“方法”,那么你就会有一个“构造函数”,它返回一个指向一个结构(将其视为其他 OO 语言中的“对象引用”)。 Mutator 方法必须是指向结构类型的方法而不是结构类型本身,以便更改“对象”的字段,因此使用指向结构而不是结构的指针很方便值本身,以便所有“方法”都在其方法集中。

    例如,在 Java 中模仿这样的东西:

    class Car {
      String make;
      String model;
      public Car(String myMake) { make = myMake; }
      public setMake(String newMake) { make = newMake; }
    }
    

    你经常会在 Go 中看到这样的东西:

    type Car struct {
      make string
      model string
    }
    func NewCar(myMake string) *Car {
      return &Car{myMake, ""}
    }
    func (self *Car) setMake(newMake string) {
      self.make = newMake
    }
    

    【讨论】:

    • 那么,我不应该使用 new 关键字吗?我应该只返回一个参考,如你所说明的那样?我被你的例子弄糊涂了。
    • @Carson: 好吧,你可以使用new,但是new 将值初始化为类型的零值,对于结构体来说,所有字段都初始化为零值。这可能不是所有类型的有效初始化值。如果零值可以初始化您的类型,那么您可以使用new;但是如果你的类型需要一个自定义的“构造函数”,那么你应该像我展示的那样定义你自己的函数
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    • 2014-09-09
    • 2010-12-01
    相关资源
    最近更新 更多