Go语言编程笔记3:结构体

Go语言编程笔记3:结构体

Go语言的另一大特色是没有类,因此也不会有继承或者多态之类的面向对象特性。这倒是让我相当诧异,因为我接触过的几乎所有的编程语言,如C++\Java\PHP\Python等都是支持面向对象的,一门09年诞生的语言却不支持面向对象,的确相当奇怪。

当然Go语言中并不是没有类似的东西——结构体,只不过在使用上和类有一些差别。

声明和初始化

结构体的声明和初始化并不难:

package main

import "fmt"

type Student struct {
	name string
	age  int
}

func main() {
	student1 := Student{
		name: "Brus Lee",
		age:  11,
	}
	fmt.Println(student1.name, "is", student1.age, "years old")
}

// Brus Lee is 11 years old

需要注意的有这么几个地方:

  • 结构体使用关键字typestruct声明,这点和C中的结构体定义类似。
  • 结构体声明时其中的结构体变量不需要使用var关键字。
  • 结构体创建并初始化时的写法类似于Javascript或PHP中的数组或JSON数据的写法,不同的是最后一个键值对必须在结尾使用,

方法

和C不同的是,Go语言中的结构体是可以定义方法的:

package main

import "fmt"

type Student struct {
	name string
	age  int
}

func (self *Student) print() {
	fmt.Println(self.name, "is", self.age, "years old.")
}

func main() {
	student1 := Student{
		name: "Brus Lee",
		age:  11,
	}
	student1.print()
}

// Brus Lee is 11 years old

要关联函数到结构体上,只需要在函数签名前加上(self *Student)这样的结构体,事实上self是一个指向当前结构体变量的指针,有点类似于C或C++中对象的this指针。然后就可以通过结构体变量调用函数,并且在函数中使用结构体指针。

赋值和传参

虽然说加上方法后结构体就很像是类了,但还是有一个显著区别,就是赋值和传参时的表现不同。

我们知道,为了节省内存开销,对象在赋值和传参时都是“引用传递”,实质上就是传递的是指针。但结构体不同,和C中的结构体一样,Go语言中的结构体对应的变量实体,在赋值和传参时都是“值传递”:

package main

import "fmt"

type Student struct {
	name string
	age  int
}

func (self *Student) print() {
	fmt.Println(self.name, "is", self.age, "years old.")
}

func change_student(s Student) {
	s.age = 1000
}

func main() {
	student1 := Student{
		name: "Brus Lee",
		age:  11,
	}
	student2 := student1
	student2.age = 20
	student2.name = "Jack Chen"
	student1.print()
	student2.print()
	change_student(student2)
	student2.print()
}

// Brus Lee is 11 years old.
// Jack Chen is 20 years old.
// Jack Chen is 20 years old.

因为是值传递,所以student2只是student1的一个“拷贝”,它们本质上是不同的两个结构体,所以自然互不影响,而类似的,在默认情况下传参时候也是“值传递”,接收到的参数实际上也是一个结构体的拷贝,所以修改参数s并不能对原始结构体产生影响。如果我们要修改原始结构体,则需要使用“引用传递”,或者用C或C++的风格来说就是“传递指针”:

package main

import "fmt"

type Student struct {
	name string
	age  int
}

func (self *Student) print() {
	fmt.Println(self.name, "is", self.age, "years old.")
}

func change_student(s *Student) {
	s.age = 1000
}

func main() {
	student1 := Student{
		name: "Brus Lee",
		age:  11,
	}
	student2 := &student1
	student2.age = 20
	student2.name = "Jack Chen"
	student1.print()
	student2.print()
	change_student(student2)
	student2.print()
}

// Jack Chen is 20 years old.
// Jack Chen is 20 years old.
// Jack Chen is 1000 years old.

在这个例子中,student2是一个student1的指针,所以两者实际上指向的是同一个结构体变量,修改其中一个就会影响到另一个。而change_student函数参数也接收的是指针,自然可以修改指针指向的变量内容。

关于为什么Go语言没有将结构体传递和赋值的默认行为设置为引用传递,我猜测大概是为了和C语言中的结构体保持一致性。总之我认为对结构体这种实际上承担了类的用途的复杂变量,尽量使用应用传递来减少内存消耗是一个不错的习惯。

构造函数

遗憾的是,Go语言中的结构体并没有构造函数这种东西,不过我们可以简单的使用一个工厂函数来进行替代:

package main

import "fmt"

type Student struct {
	name string
	age  int
}

func (self *Student) print() {
	fmt.Println(self.name, "is", self.age, "years old.")
}

func new_student(name string, age int) *Student {
	return &Student{
		name: name,
		age:  age,
	}
}

func change_student(s *Student) {
	s.age = 1000
}

func main() {
	student1 := new_student("Brus Lee", 12)
	student2 := new_student("Jack Chen", 20)
	student1.print()
	student2.print()
}

// Brus Lee is 12 years old.
// Jack Chen is 20 years old.

出于节省不必要的内存开销考虑,这里在new_student工厂函数中返回了一个Student的指针,当然也可以直接返回一个结构体,并进行拷贝,并不会影响实际逻辑。

结构体的字段

当然,结构体的自传除了是基本类型外,也可以是结构体,比如我们可以利用结构体构建一个二叉树:

package main

import "fmt"

type Node struct {
	left    *Node
	right   *Node
	content string
}

func main() {
	root := Node{
		left:    &Node{left: nil, right: nil, content: "left_child"},
		right:   &Node{left: nil, right: nil, content: "right_child"},
		content: "root",
	}
	fmt.Println(root.content)
	fmt.Println(root.left.content)
	fmt.Println(root.right.content)
}

// root
// left_child
// right_child

new

虽然Go语言并没有类和面向对象的概念,但有一个和其它语言中new关键字相似的内置函数new(),其用途是新建一个指定的结构体变量,并返回其的指针:

package main

import "fmt"

type Student struct {
	name string
	age  int
}

func (self *Student) print() {
	fmt.Println(self.name, "is", self.age, "years old.")
}

func main() {
	student1 := new(Student)
	student2 := new(Student)
	student1.name = "Jack Chen"
	student1.age = 20
	student2.name = "Brus Lee"
	student2.age = 15
	student1.print()
	student2.print()
}

// Jack Chen is 20 years old.
// Brus Lee is 15 years old.

实际上new(Student)等效于&Student{},在Go语言中通过两种方式中的任意一种来创建一个新的结构体都是可以的。蛋一般来说后者可能更常用,因为可以在创建的时候指定结构体中的字段初始化值。

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值