Go语言学习笔记-Go语言结构体
Go语言结构体
1. 结构体创建、访问与修改
1.1 定义结构体
//定义user结构体
type user struct {
id int
score float32
enrollment time.Time
name, addr string //多个字段类型相同时可以简写到一行里
}
1.2 声明和初始化结构体
var u user //声明,会用相应类型的默认值初始化struct里的每一个字段
u = user{} //用相应类型的默认值初始化struct里的每一个字段
u = user{id: 3, name: "xiaoming"} //赋值初始化
u = user{4, 100.0, time.Now(), "xiaoming", "beijing"} //赋值初始化,可以不写字段名,但需要跟结构体定义里的字段顺序一致
1.3 访问与修改结构体
u.enrollment = time.Now() //给结构体的成员变量赋值
fmt.Printf("id=%d, enrollment=%v, name=%s\n", u.id, u.enrollment, u.name)//访问结构体的成员变量
1.4 成员函数(方法)
//可以把user理解为hello函数的参数,即hello(u user, man string)
func (u user) hello(man string) {
fmt.Println("hi " + man + ", my name is " + u.name)
}
//函数里不需要访问user的成员,可以传匿名,甚至_也不传
func (_ user) think(man string) {
fmt.Println("hi " + man + ", do you know my name?")
}
1.5 为自定义类型添加方法
type UserMap map[int]User //自定义类型
//可以给自定义类型添加任意方法
func (um UserMap) GetUser(id int) User {
return um[id]
}
结构体的可见性:
- go语言关于可见的统一规则:大写字母开头跨package也可以访问;否则只能本package内部访问。
- 结构体名称以大写开头时,package外部可见,在此前提下,结构体中以大写开头在成员变量或成员方法在package外部也可见。
1.6 匿名结构体
var stu struct { //声明stu是一个结构体,但这个结构体是匿名的
Name string
Addr string
}
stu.Name = "zcy"
stu.Addr = "bj"
匿名结构体通常用于只使用一次的情况。
1.7 结构体中含有匿名成员
type Student struct {
Id int
string //匿名字段
float32 //直接使用数据类型作为字段名,所以匿名字段中不能出现重复的数据类型
}
var stu = Student{Id: 1, string: "zcy", float32: 79.5}
fmt.Printf("anonymous_member string member=%s float member=%f\n", stu.string, stu.float32) //直接使用数据类型访问匿名成员
2. 结构体指针
2.1 创建结构体指针
var u User
user := &u //通过取址符&得到指针
user = &User{ //直接创建结构体指针
Id: 3, Name: "zcy", addr: "beijing",
}
user = new(User) //通过new()函数实体化一个结构体,并返回其指针
构造函数(go语言中实际是没有构造函数的)
//构造函数。返回指针是为了避免值拷贝
func NewUser(id int, name string) *User {
return &User{
Id: id,
Name: name,
addr: "China",
Score: 59,
}
}
2.2 方法接收指针
//user传的是值,即传的是整个结构体的拷贝。在函数里修改结构体不会影响原来的结构体
func hello(u user, man string) {
u.name = "杰克"
fmt.Println("hi " + man + ", my name is " + u.name)
}
//传的是user指针,在函数里修改user的成员会影响原来的结构体
func hello2(u *user, man string) {
u.name = "杰克"
fmt.Println("hi " + man + ", my name is " + u.name)
}
//把user理解为hello()的参数,即hello(u user, man string)
func (u user) hello(man string) {
u.name = "杰克"
fmt.Println("hi " + man + ", my name is " + u.name)
}
//可以理解为hello2(u *user, man string)
func (u *user) hello2(man string) {
u.name = "杰克"
fmt.Println("hi " + man + ", my name is " + u.name)
}
3. 结构体嵌套
type user struct {
name string
sex byte
}
type paper struct {
name string
auther user //结构体嵌套
}
p := new(paper)
p.name = "论文标题"
p.auther.name = "作者姓名"
p.auther.sex = 0
type vedio struct {
length int
name string
user//匿名字段,可用数据类型当字段名
}
结构体嵌套时字段名冲突的问题
v := new(vedio)
v.length = 13
v.name = "视频名称"
v.user.sex = 0 //通过字段名逐级访问
v.sex = 0 //对于匿名字段也可以跳过中间字段名,直接访问内部的字段名
v.user.name = "作者姓名" //由于内部、外部结构体都有name这个字段,名字冲突了,所以需要指定中间字段名
4. 深拷贝与浅拷贝
type User struct {
Name string
}
type Vedio struct {
Length int
Author User
}
Go语言里的赋值都会发生值拷贝。
type User struct {
Name string
}
type Vedio struct {
Length int
Author *User
}
- 深拷贝,拷贝的是值,比如Vedio.Length。
- 浅拷贝,拷贝的是指针,比如Vedio.Author。
- 深拷贝开辟了新的内存空间,修改操作不影响原先的内存。
- 浅拷贝指向的还是原来的内存空间,修改操作直接作用在原内存空间上。
结构体slice传参
传slice,对sclice的3个字段进行了拷贝,拷贝的是底层数组的指针,所以修改底层数组的元素会反应到原数组上。
users := []User{{Name: "康熙"}}
func update_users(users []User) {
users[0].Name = "光绪"
}
本次课程演示代码
//main.go
package main
import struct2 "week03/struct"
//type User struct {
// Name string
//}
//
//func update_users(users []*User) {
// users[0].Name = "光绪"
//}
func main() {
//struct2.Init_struct()
//struct2.TestSelftype()
//b := struct2.Bird{}
//b.Walk()
struct2.TestStructPoint()
//users := []User{{Name: "康熙"}, {Name: "小明"}} //光绪
//a := User{Name: "康熙"}
//b := User{Name: "小明"}
//users := []*User{&a, &b} //发生了拷贝
//update_users(users)
//fmt.Println(users[0].Name) //光绪
//fmt.Println(a.Name) //康熙
}
//basic.go
package struct2
import "fmt"
type student struct {
Id int
Name, address string
}
func (_ student) Walk() {
fmt.Println("runing")
}
type Bird struct{}
func (Bird) Walk() {
fmt.Println("fly")
}
func (a student) sing(man string) {
fmt.Println("hello " + man + " " + a.address)
}
func Init_struct() {
var a student
fmt.Printf("%d %s %s\n", a.Id, a.Name, a.address) //0
a = student{}
fmt.Printf("%d %s %s\n", a.Id, a.Name, a.address) //0
a = student{Id: 4}
fmt.Printf("%d %s %s\n", a.Id, a.Name, a.address) //4
a.Id = 7
a.Name = "xiaoming"
fmt.Printf("%d %s %s\n", a.Id, a.Name, a.address) //7 xiaoming
}
type A struct {
Id int
}
var ccc struct {
Id int
Name string
}
func f() {
var sss A
fmt.Println(sss.Id)
var ddd A
fmt.Println(ddd.Id)
fmt.Println(ccc.Id)
}
//sp.go
package struct2
import "fmt"
func hello1(u student) {
u.Id = 888
fmt.Println(u.Id) //888
}
func hello2(u *student) {
u.Id = 888
fmt.Println(u.Id) //888
}
func (u student) hello1() {
u.Id = 888
fmt.Println(u.Id) //888
}
func (u *student) hello2() {
u.Id = 888
fmt.Println(u.Id) //888
}
func TestStructPoint() {
var s student //s.Id=0
//hello1(s)
s.hello1()
fmt.Println(s.Id) //0
//hello2(&s)
s.hello2()
fmt.Println(s.Id) //888
}
//self_type.go
package struct2
import (
"fmt"
)
type AAA int
func (AAA) Say() {
fmt.Println("say")
}
func (AAA) Hello() {
fmt.Println("hello")
}
func TestSelftype() {
var v AAA
v = 345
fmt.Printf("%d\n", v)
v.Say()
v.Hello()
}
// 构造函数
func NewStrudent(id int) *student {
s := student{}
s.Id = id
s.address = "BJ"
s.Name = "小张"
return &s
}
func testConstruct() {
a := NewStrudent(100)
fmt.Println(a.address)
}
//父类
type Animal struct {
}
//子类
type Human struct {
}
// 匿名时候
type Vedio struct {
Name string
student
}
func TsetNest() {
v := Vedio{Name: "tlbb"}
v.student = student{Id: 123, Name: "jy", address: "sh"}
fmt.Println(v.Name) //tlbb
fmt.Println(v.student.Id) //123
fmt.Println(v.Id) //Id和address本来是student成员,现在Vedio可以直接访问,即Vedio直接拥有了Id和address,形式上等价于Vedio继承了student
fmt.Println(v.address) //sh
fmt.Println(v.student.Name) //jy
}
// 不匿名时候
//type Vedio struct {
// Name string
// s student
//}
//
//func TsetNest() {
// v := Vedio{Name: "tlbb"}
// v.s = student{Id: 123, Name: "jy", address: "sh"}
// fmt.Println(v.Name) //tlbb
// fmt.Println(v.s.Id) //123
// fmt.Println(v.s.Id) //123
// fmt.Println(v.s.address) //sh
// fmt.Println(v.s.Name) //jy
//}