Go-再论struct

简介

Go-Structure对struct做了简单的介绍,本文参考如下书目继续丰富本专题。

  • 《Web Development with Go》 Chapter 3
  • 《The Go Programming Language》4.4 Page99

pointer and nonpointer method receiver

Go-Structure一文给出的示例代码中,使用的都是pointer receiver。事实上,也可以使用nonpointer receiver。事实上,也可以使用nonpointer

示例:

package main 

import (
    "fmt"
)

type Point struct {
    x, y int 
}

func (p Point) GetX() int {
    return p.x
}

func (p Point) GetY() int {
    return p.y
}

func (p *Point) SetX(ix int) {
    (*p).x = ix
}

func (p *Point) SetY(iy int) {
    (*p).y = iy
}

func (p *Point) SetY2(iy int) {
    p.y = iy
}

func (p Point) SetY3(iy int) {
    p.y = iy
}

func test1() {
    p := Point{1, 2}
    fmt.Print(p, "\t")

    fmt.Print("x=", p.GetX(), "\t")
    fmt.Print("y=", p.GetY(), "\t")

    p.SetX(3)
    p.SetY(4)
    fmt.Print(p, "\t")

    (&p).SetX(5)
    (&p).SetY(6)
    fmt.Print(p, "\t")

    fmt.Println()
}

// SetY() -> SetY2()
func test2() {
    p := Point{1, 2}
    fmt.Print(p, "\t")

    fmt.Print("x=", p.GetX(), "\t")
    fmt.Print("y=", p.GetY(), "\t")

    p.SetX(3)
    p.SetY2(4)
    fmt.Print(p, "\t")

    (&p).SetX(5)
    (&p).SetY2(6)
    fmt.Print(p, "\t")

    fmt.Println()
}

// SetY() -> SetY3()
func test3() {
    p := Point{1, 2}
    fmt.Print(p, "\t")

    fmt.Print("x=", p.GetX(), "\t")
    fmt.Print("y=", p.GetY(), "\t")

    p.SetX(3)
    p.SetY3(4)
    fmt.Print(p, "\t")

    (&p).SetX(5)
    (&p).SetY3(6)
    fmt.Print(p, "\t")

    fmt.Println()
}

func test4() {
    p := &Point{1, 2}
    fmt.Print(p, "\t")

    fmt.Print("x=", p.GetX(), "\t")
    fmt.Print("y=", p.GetY(), "\t")

    p.SetX(3)
    p.SetY(4)
    fmt.Print(p, "\t")

    //calling method SetX with receiver &p (type **Point) requires explicit dereference
    //(&p).SetX(5)
    //(&p).SetY(6)
    //fmt.Print(p, "\t")

    fmt.Println()
}
/*
D:\examples>go run helloworld.go
test1: {1 2}    x=1     y=2     {3 4}   {5 6}
test2: {1 2}    x=1     y=2     {3 4}   {5 6}
test3: {1 2}    x=1     y=2     {3 2}   {5 2}
test4: &{1 2}   x=1     y=2     &{3 4}

D:\examples>
*/
func main() {
    fmt.Print("test1: ")
    test1()

    fmt.Print("test2: ")
    test2()

    fmt.Print("test3: ")
    test3()

    fmt.Print("test4: ")
    test4()
}

要点

copied from:《Web Development with Go》 Chapter 3, Page 39

If you want to modify the data of a receiver from the method, the receiver must be a pointer

If the struct has a pointer receiver on some its methods, it is better to use it for the rest of the methods because it enables better consistency and predictability for the struct behaviors.

pointer & dot notation

这里再介绍指针类型和.操作符的特殊意义。在前面的例子中,有如下代码:

func (p *Point) SetY2(iy int) {
    p.y = iy
}

其等价于:

func (p *Point) SetY2(iy int) {
    (*p).y = iy
}

这种语法是Go中非常特殊的一点。这种语法的证据源于《The Go Programming Language》4.4 Page 100,代码如下:

type Employee struct {
    ID        int
    Name      string
    Address   string
    DoB       time.Time
    Position  string
    Salary    int
    ManagerID int
}

The dot notation also works with a pointer to a struct:

var employeeOfTheMonth *Employee = &dilbert
employeeOfTheMonth.Position += " (proactive team player)"

The last statement is equivalent to

(*employeeOfTheMonth).Position += " (proactive team player)"

Go指针的这种语法可以简化代码。

结构自嵌套

一个结构S内部不能有S成员,但可以有指向S的成员(指针 *S).

type tree struct {
    value int
    left, right *tree
}

比较结构体对象

Go中不能重载==操作符,对于要比较(同一种)结构体的两个对象,则要求其每个field都是可以比较的。而且在比较的时候,是按照每个field依次比较。也可以自定义比较的函数,比如Equals()。

package main 

import (
    "fmt"
)

type Point struct {
    x, y int 
}

func (p Point) GetX() int {
    return p.x
}

func (p Point) GetY() int {
    return p.y
}

func (p *Point) SetX(ix int) {
    p.x = ix
}

func (p *Point) SetY(iy int) {
    p.y = iy
}

func (p *Point) Equals(point Point) bool {
    return p.x == point.x
}

func Equals(p1 Point, p2 Point) bool {
    return p1.x == p2.x
}

func main() {
    p1 := Point{1, 2}
    p2 := Point{x:1, y:2}
    p3 := Point{1, 3}

    fmt.Println(p1 == p2, p2 == p3) // true, false
    fmt.Println(p1.x == p2.x && p1.y == p2.y) // true. Equals to "p1 == p2"
    fmt.Println(p1.Equals(p3)) // true
    fmt.Println(Equals(p2, p3)) // true
}

empty struct

struct中可以是空成员。

package main 

import "fmt"

type empty struct {
}

func (e* empty) Println() {
    fmt.Println("empty.Println()")
}

func main() {
    e := empty{}
    e.Println()
}

运行结果:

$ go run ex1.go 
empty.Println()
$ 

性能

和C/C++一样,在传递struct对象的时候,涉及到ctor/dector。所以,对于大的struct,最好传递指针。

Page 103 《The Go Programming Language》:

For efficiency, larger struct types are usually passed to or returned from functions indirectly using a pointer.

匿名成员 anonymous field

package main 

import "fmt"

type Point struct {
    X, Y int 
}

type Circle struct {
    Center Point
    Radius int 
}

type Wheel struct {
    Circle Circle
    Spokes int 
}

func foo() {
    var w Wheel
    w.Circle.Center.X = 8
    w.Circle.Center.Y = 8 
    w.Circle.Radius = 5 
    w.Spokes = 20

    fmt.Println("foo():", w)
}


type Circle2 struct {
    Point // anonymous fields with a named type
    Radius int 
}

type Wheel2 struct {
    Circle2
    Spokes int 
}

func bar() {
    var w Wheel2
    w.X = 18
    w.Y = 18 //w.Circle2.Point.Y = 18 
    w.Radius = 15 
    w.Spokes = 120

    fmt.Println("bar():", w)
}

type Wheel3 struct {
    *Point
    Radius int
}

func baz() {
    var w Wheel3
    w = Wheel3{&Point{28, 28}, 25}

    fmt.Printf("baz(): %#v\n", w)
}

/*
foo(): {{{8 8} 5} 20}
bar(): {{{18 18} 15} 120}
baz(): main.Wheel3{Point:(*main.Point)(0xc420072070), Radius:25}
*/
func main() {
    foo()
    bar()
    baz()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值