小白 对于go的基础认知-2 |青训营

32 阅读4分钟

上一文介绍了对于变量常量和运算符的基础认识
以下是本人对结构体相关内容的的学习笔记

结构体

用来表示一组不同数据的集合
结构体占用一块连续的内存
自定义变量,可以记录他的各项属性
结构体的定义

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("张三"18true)
   p2:=newoerson("李四",19,false)
   p3:=newperson("王五"20true)
   ....
}

方法与接收者(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("张三"18true)
   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() //乐乐会动!