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,可以直接这样写
	//深拷贝浅拷贝可以理解为拷贝的函数,深拷贝是涉及到指针
}
posted @ 2022-05-27 15:09  liwenchao1995  阅读(65)  评论(0编辑  收藏  举报