go 结构体
go 结构体
1.1 结构体
我们把一个结构体的函数称为方法,和函数的区别是 func (u user) hello(man string) {},多了(u user)
1.1.1 定义结构体
type user struct {
id int
score float32
enrollment time.Time
name, addr string //多个字段类型相同时可以简写到一行里
}
1.1.2 声明和初始化结构体
var u user //声明,会用相应类型的默认值初始化struct里的每一个字段
u = user{} //用相应类型的默认值初始化struct里的每一个字段
u = user{id: 3, name: "zcy"} //赋值初始化
u = user{4, 100.0, time.Now(), "zcy", "beijing"} //赋值初始化,可以不写字段名,但需要跟结构体定义里的字段顺序一致
1.1.3 访问与修改结构体
u.enrollment = time.Now() //给结构体的成员变量赋值
fmt.Printf("id=%d, enrollment=%v, name=%s\n", u.id, u.enrollment, u.name)//访问结构体的成员变量
1.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.1.5 为自定义类型添加方法
type UserMap map[int]User //自定义类型
//可以给自定义类型添加任意方法
func (um UserMap) GetUser(id int) User {
return um[id]
}
1.1.6 结构体的可见性
- go语言关于可见的统一规则:大写字母开头跨package也可以访问;否则只能本package内部访问。
- 结构体名称以大写开头时,package外部可见,在此前提下,结构体中以大写开头在成员变量或成员方法在package外部也可见。
1.1.7 匿名结构体
var stu struct { //声明stu是一个结构体,但这个结构体是匿名的
Name string
Addr string
}
stu.Name = "zcy"
stu.Addr = "bj"
匿名结构体通常用于只使用一次的情况。
结构体中含有匿名成员
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.1 结构体指针
2.1.1 创建结构体指针
var u User
user := &u //通过取址符&得到指针
user = &User{ //直接创建结构体指针
Id: 3, Name: "zcy", addr: "beijing",
}
user = new(User) //通过new()函数实体化一个结构体,并返回其指针
2.1.2 构造函数
//构造函数。返回指针是为了避免值拷贝
func NewUser(id int, name string) *User {
return &User{
Id: id,
Name: name,
addr: "China",
Score: 59,
}
}
2.1.3 方法接收指针
//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.1 结构体嵌套
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//匿名字段,可用数据类型当字段名
}
3.1.1 结构体嵌套时字段名冲突的问题
v := new(vedio)
v.length = 13
v.name = "视频名称"
v.user.sex = 0 //通过字段名逐级访问
v.sex = 0 //对于匿名字段也可以跳过中间字段名,直接访问内部的字段名
v.user.name = "作者姓名" //由于内部、外部结构体都有name这个字段,名字冲突了,所以需要指定中间字段名
4.1 深拷贝与浅拷贝
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,对sclice的3个字段进行了拷贝,拷贝的是底层数组的指针,所以修改底层数组的元素会反应到原数组上
users := []User{{Name: "康熙"}}
func update_users(users []User) {
users[0].Name = "光绪"
}
5.1 代码整理
//定义一个结构体
type jgtinitelement1 struct {
id int
score float32
name,country string //多个字段类型相同时,可以简写到一行
enviroment time.Time
}
//打印成员变量
func jgtinit1() {
//var u jgtinitelement1 //会用相对应类型的默认值初始化struct里的每一个字段
//u := jgtinitelement1{} //用相应类型的默认值初始化struct里的每一个字段
//u := jgtinitelement1{id:3,name:"李文超"} //赋值初始化结构体里的id和name
//u := jgtinitelement1{4,100.0,"liwenchao","china",time.Now()}
u := jgtinitelement1{4,100.0,"liwenchao","china",time.Now()}
fmt.Printf("结构体读取成员变量name: %d,当前时间是%v\n",u.name,u.enviroment)
}
//结构体打印成员函数
//可以理解为定义了一个 jgtinit2 的一个函数,需要传参 man,前面调用了 jgtinitelement1
//func (u jgtinitelement1)jgtinit2(man string) {
//func (_ jgtinitelement1)jgtinit2(man string) { //也可以这么写—_,不调用u
//func (jgtinitelement1)jgtinit2(man string) { //或者这样,直接不写u
func (u jgtinitelement1) jgtinit2(man string) {
fmt.Printf("Hi ,%s,%s",man,u.name)
//创建结构体指针
var usid jgtinitelement1
//三种创建结构体指针的方法
userid := &usid //1通过取址符号&得到指针
userid = &jgtinitelement1 { //2直接创建结构体指针
id:3,name:"liwenchao",
}
userid = new(jgtinitelement1) //3调用new方法,先实例化一个结构体,并返回其指针
fmt.Printf("结构体指针%d",userid)
//方法接收指针
//在函数调用时,如果传入结构体,不会影响结构体.如果传入的是 *jgtinitelemenet1 指针,则会影响指针的内容
}
//这里可以自定义类型 type jgt1 map[int]jgtinitelement1 调用上面的 jgtinitelement1,相当于别名
type jt1 map[int]jgtinitelement1
//可以给自定义类型添加任意方法, (um jt1)是调用的结构体, jgtinit3是函数 (id int)是输入参数 jgtinitelement1 是返回函数
//这样可以给 jgtinitelement 添加各种类型, 相当于处理后的结构体把 um[id]返回给 jgtinitelement1
//用这种方法可以修改结构体中某个属性,并返回
func (um jt1) jgtinit3(id int) jgtinitelement1 {
return um[id]
}
//定义匿名结构体,注意下面是 var
var jgtinitelement2 struct {
Name string //这里首字母大写,在别的包引用时可以直接引用,小写则不能被直接引用
Addr string
}
//调用匿名结构体
//匿名结构体通常用在只使用一次的情况,如果调用了匿名函数,只能使用一次,不能在一个函数中调用两次了
func jgtinit4() {
jgtinitelement2.Name = "李文超"
jgtinitelement2.Addr = "中国"
fmt.Printf("匿名结构体读取成员变量%d",jgtinitelement2.Name)
}
//结构体嵌套
type jgtinitelement3 struct {
color string
jgt1 jgtinitelement1
}
//嵌套结构体
func jgtinit5() {
p := new(jgtinitelement3)
p.color = "red"
p.jgt1.name = "liwenchao" //嵌套结构体
}
//匿名嵌套结构体
type jgtinitelement4 struct{
color string
jgtinitelement1
}
func jgtinit6() {
v := new(jgtinitelement4)
v.jgtinitelement1.name = "liwenchao"
v.name = "liwenchao" //如果jgtinitelement4里没有name,可以直接这样写
//深拷贝浅拷贝可以理解为拷贝的函数,深拷贝是涉及到指针
}