- go语言的面向对象相对简单,只支持封装,不支持继承和多态,go中用面向接口来做继承和多态的任务
- go中没有class ,只有struct
- struct的创建
type treeNode struct {
value int
left, right *treeNode
}
- struct的声明和初始化
//声明一个结构体类型
var root treeNode
//初始化方式一
root = treeNode{value:3}
//初始化方式二
root.left = &treeNode{}
root.right = &treeNode{5,nil,nil}
//初始化方式三
root.right.left = new(treeNode)
//new(),只接受一个参数,这个参数是一个类型,分配好内存后,返回一个指向该类型内存地址的指针。同时请注意它同时把分配的内存置为零,也就是类型的零值。
//这里,root结构本身,而root.left是一个地址,在go中不论地址含是结构本身,一律用"."来访问成员, 而C++中,访问地址的成员要用"->"
//初始化方法四 使用工厂方法
root.right.right = creatNode(4)
nodes := []treeNode{
{value:3}, //这里相当于 treeNode{value:3}, 前面的treeNode省略掉了
{},
{6,nil,&root},
}
fmt.Println(nodes) //输出结果: [{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> 0xc420090020}]
- struct的工厂化方法,go的结构体没有构造函数一说,但我们可以给我们定义的结构体创建一个工厂方法,用来初始化结构体
- 工厂方法返回了一个局部变量地址
func creatNode(value int) *treeNode {
return &treeNode{value: value}
//这里,treeNode{value: value}是定义在函数中的,若在C++中,它是一个局部变量,是不能返回到外面给别人使用的,但在go中,局部变量可以返回出去给别人使用
}
- 为struct创建方法,"func"后可以跟一个接受者,其实这个接受者实质上也是函数的一个参数,只是调用的时候会有些区别
func (node treeNode) print() {
fmt.Println(node.value)
}
其和func print(node treeNode) {
fmt.Println(node.value)
}
本质上实现的作用一样
但前者的调用: print(root)
后者的调用: root.print()
- struct方法接收者的值传递和指针传递:
- 结构体方法的接受者,同样是传值的,想要传递引用,则声明为指针类型
- 只有将接受者设为指针,才可改变结构体的内容
func (node treeNode) setValue1 (value int){ //接收者为值传递
node.value = value
}
func (node *treeNode) setValue2 (value int){ //接收者为指针传递
node.value = value
//这里的node是一个指针类型,是一个地址,go中地址也可以通过"."来直接访问其地址所存内容的成员,而不需(*node).value 或 node->value
}
结果验证:
root.right.left.print() //输出结果:0
root.right.left.setValue1(10)
root.right.left.print() //输出结果:0 setValue1()为值传递,value没有被改掉
root.right.left.setValue2(20)
root.right.left.print() //输出结果:20 setValue2()引用传递,value被改掉//注意:这里在调用的时候,会进行人性化处理,接受者是结构指针类型或者是实际的结构类型,调用者是结构指针或实际结构体,不管如何组合都是可以混用的
(*root.right.left).print() //输出结果:20 与 root.right.left.print() 一致
- go中struct的接收者为nil指针时也可以调用方法
- go中的nil与其他语言中的null不同,null只是用来判断是不是空
func (node *treeNode) setValue3 (value int){
if node == nil{
fmt.Println("setting value to nil node!")
return
}
node.value = value
}结果验证:
var pRoot *treeNode
pRoot.setValue3(30) //输出结果:setting value to nil node!
//这里的pRoot 只有声明 是一个nil指针,但它同样可以调用setValue3()
- 值接收者VS指针接受者
- 要改变内容必须使用指针接收者
- 结构过大也考略使用指针接受者
- 一致性:如有指针接受者,最好都是指针接收者
- 值接收者是go语言特有的,其他语言都有指针接收者(Java中的this)
- 值/指针接收者均可接收值/指针
- struct是创建在堆上还是栈上?
- c++中,局部变量是分配在栈上的,函数一退出,局部变量会立刻被销毁,若想要传出去,必须在堆上分配,堆上分配的要手动释放
- Java中多使用new(),使用new()是分配在堆上的,需要有垃圾回收机制
- go中,不需要知道结构创建在哪里,go语言的编译器以及运行环境会根据需要自动检测结构需要创建在哪里,go中存在垃圾回收器,堆上分配的内容会参与垃圾回收
- 树的中序遍历
//构造一棵树`
var root treeNode
root = treeNode{value:3}
root.left = &treeNode{}
root.right = &treeNode{5,nil,nil}
root.right.left = new(treeNode)
root.right.right = creatNode(4)//中序遍历整棵树 左-中-右的顺序
func (node *treeNode) traverse(){
if node == nil{
return
}
node.left.traverse()
node.print()
node.right.traverse()
}
完整代码:
package main
import (
"fmt"
)
type treeNode struct {
value int
left, right *treeNode
}
//go的结构体没有构造函数一说,但我们可以创建一个工厂方法给我们定义的结构体
//工厂方法返回了一个局部变量地址
func creatNode(value int) *treeNode {
return &treeNode{value: value}
//这里,treeNode{value: value}是定义在函数中的,若在C++中,它是一个局部变量,是不能返回到外面给别人使用的,但在go中,局部变量可以返回出去给别人使用
}
//为结构体创建方法,"func"后可以跟一个接受者,其实这个接受者实质上也是函数的一个参数,只是调用的时候会有些区别
func (node treeNode) print() {
fmt.Println(node.value)
}
//其和 func print(node treeNode) {
// fmt.Println(node.value)
// }
//本质上实现的作用一样
//但前者的调用: print(root)
//后者的调用: root.print()
//结构体方法的接受者,同样是传值的,想要传递引用,则声明为指针类型
//只有将接受者设为指针,才可改变结构体的内容
func (node treeNode) setValue1 (value int){
node.value = value
}
func (node *treeNode) setValue2 (value int){
node.value = value
//这里的node是一个指针类型,是一个地址,go中地址也可以通过"."来直接访问其地址所存内容的成员,而不需(*node).value 或 node->value
}
//nil指针也可以调用方法
//go中的nil与其他语言中的null不同,null只是用来判断是不是空
func (node *treeNode) setValue3 (value int){
if node == nil{
fmt.Println("setting value to nil node!")
return
}
node.value = value
}
//中序遍历整棵树 左-中-右的顺序
func (node *treeNode) traverse(){
if node == nil{
return
}
node.left.traverse()
node.print()
node.right.traverse()
}
func main() {
//声明一个结构体类型
var root treeNode
//初始化方式一
root = treeNode{value:3}
//初始化方式二
root.left = &treeNode{}
root.right = &treeNode{5,nil,nil}
//初始化方式三
root.right.left = new(treeNode)
//new(),只接受一个参数,这个参数是一个类型,分配好内存后,返回一个指向该类型内存地址的指针。同时请注意它同时把分配的内存置为零,也就是类型的零值。
//这里,root结构本身,而root.left是一个地址,在go中不论地址含是结构本身,一律用"."来访问成员, 而C++中,访问地址的成员要用"->"
//初始化方法四 使用工厂方法
root.right.right = creatNode(4)
nodes := []treeNode{
{value:3}, //这里相当于 treeNode{value:3}, 前面的treeNode省略掉了
{},
{6,nil,&root},
}
fmt.Println(nodes) //输出结果: [{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> 0xc420090020}]
root.print() //输出结果: 3
root.right.left.print() //输出结果:0
root.right.left.setValue1(10)
root.right.left.print() //输出结果:0 setValue1()为值传递,value没有被改掉
root.right.left.setValue2(20)
root.right.left.print() //输出结果:20 setValue2()引用传递,value被改掉
//这里在调用的时候,会进行人性化处理,接受者是结构指针类型或者是实际的结构类型,调用者是结构指针或实际结构体,不管如何组合都是可以混用的
(*root.right.left).print() //输出结果:20
var pRoot *treeNode
pRoot.setValue3(30) //输出结果:setting value to nil node!
//这里的pRoot 只有声明 是一个nil指针,但它同样可以调用setValue3()
root.traverse() //输出:0 3 20 5 4
}