面向对象,有封装、继承、多态三大特性,而C++支持多继承,ruby为防止混乱不支持多继承而采用mixin的,C#则支持单继承并且推荐使用接口。可见各种语言的设计者对于OOP的理解各有不同,而go语言仅仅保存了面向对象的封装,不支持继承和多态,秉承了简介的设计理念。
本篇将会通过实现一个树来介绍go语言所谓的“面向对象的用法”:
定义结构体:
type treeNode struct {
value int
left, right *treeNode
}
如上所示,我们定义了一个包含一个int成员和左右两个结点的树结构体。
实例化:
func main(){
//初始化方式1:{0 <nil> <nil>}
var root treeNode
root = treeNode{value:3}
root.left = &treeNode{}
root.right = &treeNode{5,nil,nil}
root.right.left = new(treeNode)
nodes := []treeNode{
{value:3},
{},
{6,nil,&root},
}
fmt.Println(nodes)
}
与C++语言不同,无论是地址还是结构本身,都使用.来访问成员。
go语言没有构造函数,但如果想控制结构体的构造,我们可以使用工厂函数。而工厂函数在go语言中不过是普通的函数。
添加工厂函数:
func createNode(value int) *treeNode{
return &treeNode{value: value}
}
- 使用了自定义工厂函数
- 注意:返回了局部变量的地址!
结构创建在哪里?
在C++中,局部变量分配在栈上,通过new的则分配在堆上。而Java中,都分配在堆上,靠垃圾回收机制。
而go语言分配在哪?
不需要知道:
如果函数取得地址并返回给别人用,则程序会分配在堆上。否则很有可能会被分配在栈上。
为结构体定义方法:
在Go语言中,方法不写在结构体中。为此,我们需要在定义函数时,在函数名前写一个接收者(这也是个值传递):
func (node treeNode) print(){
fmt.Println(node.value)
}
于是可以这样调用函数:
root.print() 由于Go语言中的参数传递都是值传递,因此如果需要修改结构体的值,则需要传递一个指针:
func (node *treeNode) setValue(value int){
node.value = value
}
编译器可以智能地获取指针或者值,但如果不用指针,只能是值传递。
nil指针也能调用方法。
添加遍历函数:
func (node *treeNode) traverse(){
if node == nil{
return
}
node.left.traverse()
node.print()
node.right.traverse()
}
使用中序遍历,实现遍历函数。
打印结果为0,2,3,4,5