结构体和方法
go语言仅支持封装,不支持多态和继承,使用接口来实现。
因此只有struct,没有class。
一律使用点来访问成员。
type treeNode struct {
value int
left, right *treeNode
}
func main() {
var root treeNode
//go语言提供了非常多的构造方法,因此没有构造函数的说法
root = treeNode{value:3}
root.left = &treeNode{}
root.right = &treeNode{5, nil,nil}
root.right.left = new(treeNode) //创建空的treeNode,返回地址
nodes := []treeNode{
{value:4},
{},
{6,nil,&root},
}
fmt.Println(nodes)
}
结构的创建,注意返回的是局部变量的地址。
在C++中,局部变量是分配在栈上的,因此在返回之后,这个栈上的数据会被销毁,若要使得返回之后还存在,必须在堆上进行分配,但是在堆上分配必须进行手动释放。
在go语言中,结构创建是在堆上还在栈上?不需要知道,这个是由编译器和运行时的环境来决定的,如果下面的treeNode没有取地址并且返回的话,编译器就有可能将其分配在栈上,若下面的treeNode取地址并且返回的话,编译器就有可能将其分配在堆上,就会参与垃圾回收。
//可以提供工厂函数
func createNode(value int) *treeNode{
return &treeNode{value:value} //这里返回的是局部变量的地址,如果在C++中程序会挂掉,go语言中不会挂
}
go语言给结构体定义方法,是放在结构体外面的。在函数名字的前面有一个小括号,相当于其他语言中的this。
//方法的定义 vs 函数的定义
func (node treeNode) print() {
fmt.Println(node.value)
}
func print1(node treeNode) {
fmt.Println(node.value)
}
//这两个是一样的,只是语法上不同
root.print()
print1(root)
//也是进行值传递,这对原来的value是改变不了的。
func (node treeNode) setValue() {
node.value = 100
}
func (node *treeNode) setValue(value int) {
if node == nil{
fmt.Println("nil")
return
}
node.value = value //不需要写成(*node)->value = value
}
在go语言中,对于值接收者和指针接收者,要值就拷贝一份,要指针就取地址。
- 要改变内容的话,必须使用指针接收者;
- 结构过大的话,也需要考虑使用指针接收者;
- 一致性:如果有指针接收者,最后使用指针接收者;
- 值接收者是go语言特有的,C++的this是指针接收者;
- 值/指针接收者均可以接收值/指针接收者。
nil指针也可以调用方法!
root = treeNode{value:3}
root.print()
root.setValue(100) // 而不需要写成(&root)->setValue()
var pRoot *treeNode
pRoot.setValue(200)
包和封装
名字一般使用CamelCase
首字母大写:public
首字母小写:private
每个目录只能是一个包名,main包包含可执行入口
为结构定义的方法必须放在同一个包内,可以是不同的文件
如何扩充系统类型或者别人的类型
- 定义别名
- 使用组合
//使用组合
type myTreeNode struct {
node *tree.TreeNode
}
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.node == nil{
return
}
left := myTreeNode{myNode.node.Left}
right := myTreeNode{myNode.node.Right}
left.postOrder()
right.postOrder()
myNode.node.Print()
}
//重命名
type Queue []int
func (q *Queue) Push(v int) {
*q = append(*q,v) //使用系统的类型的时候,必须加上*号来解引用
}
func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head
}
func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}
GOPATH环境变量
默认在~/go(unix, linux),%USERPROFILE%\go(windows)
官方推荐:所有项目和第三方库都放在同一个GOPATH下。也可以将每个项目放在不同的GOPATH下。
由于网络的原因,使用go get通常无法获取国外服务器上的文件。
可以先使用go get下载gopm, go get -v github.com/gpmgo/gopm。
使用gopm下载好了包之后,使用go install来安装,产生pkg文件和可执行文件,之后的可执行文件放在了$GOPATH/bin中。go build进行编译。go run直接编译运行。
使用goimports工具可以在保存文件的时候,自动整理go文件中的import内容,将没有用到的库删除,或者自动引入别的库。
通过gopm get xxx,可以将指定的包下载到gopm的本地仓库~/.gopm/repos(建议使用) 通过’gopm get -g xxx’,可以将指定的包下载到GOPATH下。(建议使用) 通过’gopm get -l xxx’,可以将指定的包下载到当前所在目录(不常用)。
如gopm get -g golang.org/x/net