上一文介绍了对于变量常量和运算符的基础认识
以下是本人对结构体相关内容的的学习笔记
结构体
用来表示一组不同数据的集合
结构体占用一块连续的内存
自定义变量,可以记录他的各项属性
结构体的定义
type person struct {
Name string
Age int
Sex bool
}
/* 使用“.”访问结构体成员(与c相同)
结构体的指针与普通指针一样,都是指向这个类型变量的地址
*/
结构体的实例化
参考代码 结构体变量的实例化与普通变量相同
var A person
结构体还支持通过new函数进行实例化
var A=new(person)
A.Name="张三"
A.Age=18
A.Sex=true
fmt.Printf("A=%v\n",A)//A={张三 18 true}
fmt.Printf("A=%#v\n",A)//A=main.person{Name:"张三", Age:18, Sex:true}
这时的A是一个结构体指针,而非结构体变量。在golang中支持结构体指针支持直接使用.来访问结构体中的成员
var A=new(person)
A.Name="张三"
A.Age=18
A.Sex=true
fmt.Printf("A=%v\n",A)//A=&{张三 18 true}
fmt.Printf("A=%#v\n",A)//A=&main.person{Name:"张三", Age:18, Sex:true}
比直接声明多了个&,代表A此时是一个指针
结构体还可通过取地址的方式进行实例化
var A=&person{}
fmt.Printf("%T\n",A)//*main.person
fmt.Printf("A=%#v\n",A)//A=&main.person{Name:"", Age:0, Sex:false}
A.Name="张三"
A.Age=18
A.Sex=true
fmt.Printf("A=%v\n",A)//A=&{张三 18 true}
fmt.Printf("A=%#v\n",A)//A=&main.person{Name:"张三", Age:18, Sex:true}
和new实例化后输出的结果没有区别,事实通过地址进行实例化和new实例化时等价的
匿名结构体
这种结构体多用于定义临时变量
var user struct{Name string;Age int}
user.Name="李华"
user.Age="18"
fmt.Printf("%v",user)//输出结果为: {李华 18}
fmt.Printf("%#v",user)//输出结果为:struct { Name string; Age int }{Name:"李华", Age:18}
结构体的初始化
- 使用键值对或指针-键值对初始化
p1:= person{
Name:"张三",
Age:18,
Sex:true,
}
p2:= &person{
Name:"张三",
Age:18,
Sex:true,
}
p3:=&person{//对部分值进行初始化
Age:18,
}
p4:=&person{//等价于 var p4=&person{}
}
- 使用值列表进行初始化
p1:=&person {
"张三",
18,
true,
}
这种方式必须初始化结构体的所有字段。
初始值的填充顺序与声明顺序必须一致。
该方式不能和键值初始化方式混用
结构体的构造函数
其实这个东西本身不存在,但是可以自己diy一个用来减少代码量,免得向上面一样敲好几个花括号
如下
func newperson(name string,age int,sex bool) *person{//采用指针的原因是因为结构体的数据量普遍较大,直接进行值传递的开销很大,改用地址可以提高效率
return p:=&person{
Name:name,
Age:age,
sex:Sex,
}
}
func main(){
p1:=newperson("张三",18,true)
p2:=newoerson("李四",19,false)
p3:=newperson("王五",20,true)
....
}
方法与接收者(this?self?明明没有类的说法却要搞个这个可还行)
golang中的方法是作用域一种特殊类型变量的函数,这种特殊类型的变量就被称作接收者,作用类似于this与self
这种函数的区别在于在定义时必须在函数名之前定义形参(这就是接收者变量),函数名之后的参数可有可无
格式和例子
func (接收者变量 类型)函数名(参数 类型) 返回值{
函数体
}
func (p person)nameis(){
fmt.Println(p.name)
}
func (p *person)namechange(newname string){
p.name=newname
}
func (p person)namechange(newname string){
p.name=newname
}
func main(){
p1:=newperson("张三",18,true)
p1.nameis()//张三
p1.wrongnamechange("李四")
p1.nameis()//张三
p1.namechang("李四")
p1.nameis()//李四
}
在上面的wrongnamechange方法中,接收者是使用的值传递方式进行传递,值传递的机制是从原变量拷贝到一个新的仅存在函数中的变量,无法对原变量进行修改,而namechange方法则是使用地址传递的方法进行传递,这时做出的修改时修改对应内存地址中的值,会直接修改原变量
当具有以下三种需求时可采用指针作为形参
- 需要修改接收者中的值
- 接收者是拷贝代价比较大的大对象
- 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
结构体的匿名字段
这种字段在结构体中只有类型而没有字段名,但由于没有字段名,匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,在一个结构体中,同一个类型仅能出现一个匿名字段
//Person 结构体Person类型
type Person struct {
string
int
}
func main() {
p1 := Person{
"小王子",
18,
}
fmt.Printf("%#v\n", p1) //main.Person{string:"北京", int:18}
fmt.Println(p1.string, p1.int) //北京 18
}
结构体嵌套
这种套娃的结构体常用于配置文件的读写
"继承"
//Animal 动物
type Animal struct {
name string
}
func (a *Animal) move() {
fmt.Printf("%s会动!\n", a.name)
}
//Dog 狗
type Dog struct {
Feet int8
*Animal //通过嵌套匿名结构体实现继承
}
func (d *Dog) wang() {
fmt.Printf("%s会汪汪汪~\n", d.name)
}
func main() {
d1 := &Dog{
Feet: 4,
Animal: &Animal{ //注意嵌套的是结构体指针
name: "乐乐",
},
}
d1.wang() //乐乐会汪汪汪~
d1.move() //乐乐会动!