【问题标题】:Why should constructor of Go return address?Go的构造函数为什么要返回地址?
【发布时间】:2023-01-25 05:54:18
【问题描述】:

我知道 Go 没有任何构造函数,并且在它的位置使用了 New func,但根据 this example

func NewFile(fd int, name string) *File {
  if fd < 0 {
    return nil
  }
  f := File{fd, name, nil, 0}
  return &f
}

他们总是返回&amp;f。为什么仅仅返回 File 是不够的?

更新

我试过为一个简单的结构返回创建的对象,没问题。所以,我想知道返回地址是否是构造函数或其他东西的标准方式。

谢谢。

【问题讨论】:

  • 该函数返回一个File*,因此不可能“直接”返回File。在前面的案例中,new(File) 用于以 File* 开头。
  • 所以,除非我使用 File,否则我不必将地址返回给变量,对吗?
  • 这两个例子都使用“文件”..我不知道如何解释这个问题。
  • 在 go 中,返回一个变量的地址与在 C 中调用 malloc() 是一样的。如果你不需要它,你就不需要使用它。
  • 没有关于语言的东西需要返回指针的函数,这是定义函数的人做出的选择。您的问题只是概括为“我什么时候应该使用指针,什么时候应该使用普通结构?”答案是“视情况而定”。您可以在 Internet 上做一些研究,了解人们通常何时选择在 Go 中使用指针。

标签: pointers go constructor


【解决方案1】:

如前所述,是的,规范允许您返回值(作为非指针)或指针。这只是您必须做出的决定。

什么时候返回指针?

通常,如果您返回的值作为指针“更有用”。什么时候更有用?

例如,如果它有许多方法与指针接收器.是的,您可以将返回值存储在一个变量中,这样它将是可寻址的,您仍然可以调用其具有指针接收器的方法。但是如果立即返回一个指针,您可以“链接”方法调用。看这个例子:

type My int

func (m *My) Str() string { return strconv.Itoa(int(*m)) }

func createMy(i int) My { return My(i) }

现在写:

fmt.Println(createMy(12).Str())

会导致错误:cannot call pointer method on createMy(12)

但是,如果您返回一个指针,则可以工作:

func createMy(i int) *My { return (*My)(&i) }

此外,如果将返回值存储在不可寻址的数据结构中(例如map),则无法通过索引映射来调用值的方法,因为映射的值不可寻址。

看这个例子:My.Str() 有指针接收器。因此,如果您尝试这样做:

m := map[int]My{0: My(12)}
m[0].Str() // Error!

你不能因为“无法获取 m[0] 的地址”.但以下工作:

m := map[int]*My{}
my := My(12)
m[0] = &my // Store a pointer in the map

m[0].Str() // You can call it, no need to take the address of m[0]
           // as it is already a pointer

指针有用的另一个例子是它是一个“大”结构,将被大量传递. http.Request 是一个光辉的例子。它很大,通常会大量传递给其他处理程序,并且它有带指针接收器的方法。

如果您返回一个指针,这通常表明如果将返回值作为指针存储和传递会更好。

【讨论】:

    【解决方案2】:

    指针接收器接受指针和值类型,只要它与数据类型匹配。

    type User struct {
        name  string
        email string
        age   int
    }
    
    // NewUserV returns value ... ideally for a User we should not be 
    // returning value
    func NewUserV(name, email string, age int) User {
        return User{name, email, age}
    }
    
    // NewUserP returns pointer ...
    func NewUserP(name, email string, age int) *User {
        return &User{name, email, age}
    }
    
    // ChangeEmail ...
    func (u *User) ChangeEmail(newEmail string) {
        u.email = newEmail
    }
    
    func main() {
        // with value type
        usr1 := NewUserV("frank", "frank@camero.com", 22)
        fmt.Println("Before change: ", usr1)
        usr1.ChangeEmail("frank@gmail.com")
        fmt.Println("After change: ", usr1)
    
        // with pointer type
        usr2 := NewUserP("john", "john@liliput.com", 22)
        fmt.Println("Before change: ", usr2)
        usr2.ChangeEmail("john@macabre.com")
        fmt.Println("After change: ", usr2)
    
    }
    
    

    除了 icza 提到的关于被传递的大结构之外。指针值是一种表示指针语义在起作用的方式,使用特定类型的人不应该复制指针共享的值。

    如果您查看 File 或 http 类型的结构,它会维护该值唯一的通道或其他一些指针类型。复制值(由指针提供给您)会导致难以发现错误,因为复制的值可能最终写入或读取原始值的指针类型。

    【讨论】:

      猜你喜欢
      • 2015-11-03
      • 2011-03-21
      • 1970-01-01
      • 1970-01-01
      • 2019-02-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多