Go Method v.s. C++ Class Member Functions

按照Go语言语法规范的说法,Go的method是一个带了一个receiver的function。这个receiver的作用和C++的class member function的this指针很相似。但是receiver不一定要是指针类型,也可以是一个值类型;比如下面这个例子:

package main

import "fmt"

type Int int

func (i *Int) PrintAndIncrementWithPointer() {
	fmt.Printf("%d  ", *i)
	(*i)++
}

func (i Int) PrintAndIncrementWithValue() {
	fmt.Printf("%d  ", i)
	i++
}

func main() {
	var p *Int = new(Int)
	*p = 10
	p.PrintAndIncrementWithPointer()
	fmt.Printf("%d  ", *p)
	p.PrintAndIncrementWithValue()
	fmt.Printf("%d  ", *p)

	var a Int = 20
	a.PrintAndIncrementWithPointer()
	fmt.Printf("%d  ", a)
	a.PrintAndIncrementWithValue()
	fmt.Printf("%d  ", a)
}

从这个例子可以看出来,即便PrintAndIncrementWithPointer的receiver是Int指针,但是用指针(pointer)和用值(value)都可以调用它(第26行和第32行)。类似的,即便PrintAndIncrementWithValue的receiver是Int,但是用指针和值都能调用它(第28行和第34行)。

那么难道receiver是指针和值是没有区别的嘛?不是的。在Go FAQ中有解释

When defining a method on a type, the receiver (s in the above example) behaves exactly is if it were an argument to the method. Define the method on a pointer type if you need the method to modify the data the receiver points to. Otherwise, it is often cleaner to define the method on a value type.

意思是:如果reciever是指针,那么method就可以修改receiver指向的值的内容;否则修改对调用者无效。这也可以从上面程序的输出结果得到验证:

10  11  11  11  20  21  21  21

这个运行结果说明,第9行的++是对调用者(main)拥有的*pa起了作用的;而第14行的++则没有改变调用者拥有的数据。

上述区别类似于C++中的const/non-const class member function。上面例子用C++描述,大概是这样的:

class Int {
public:
  void PrintAndIncrementWithPointer() {
    std::cout << a_ << "  ";
    ++a_;
  }
  void PrintAndIncrementWithValue() const {
    std::cout << a_ << " ";
    // ++a_;  Note here that ++a_ is illegal here in C++ syntax
  }
private:
  int a_;
};

上面这段C++程序提醒我们,C++的class member function还有一个特性,就是外界是否可以调用(public还是protected/private)。“外界是否可以调用”在Go中不是通过专门的关键词来描述的,而是通过method name是否是大写开头(capitalized)来标志的。