返回
北京兄弟连IT
置顶
该校与厚学网暂未合作,平台不保证课程的真实有效性,如有侵权等争议,请及时与厚学网联系处理
招生热线:400-656-1390

学校地址:北京市昌平区回龙观文华西路育荣教育园区兄弟连IT教育

兄弟连Go语言培训分享Struct结构体

91 2018-08-14 14:20:00

学习笔记

兄弟连Go语言培训课程体系设计架构包括了区块链的基础语言Go语言、区块链后端技术体系、区块链公链、区块链分布式应用开发等内容讲解,以及到最后的面试指导和项目实战。课程由清华微软谷歌团队精心打造,历时半年时间共同研发而出。

  先介绍一下go语言的类型系统

 

  Golang中的类型系统

 

  类型系统是指一个语言的类型体系结构。一个典型的类型系统通常包含如下基本内容:

 

  q基础类型,如byte、int、bool、float等;

 

  q复合类型,如数组、结构体、指针等;

 

  q可以指向任意对象的类型(Any类型);

 

  q值语义和引用语义;

 

  q面向对象,即所有具备面向对象特征(比如成员方法)的类型;

 

  q接口。

 

  Go语言中的大多数类型都是值语义,并且都可以包含对应的操作方法。在需要的时候,你可以给任何类型(包括内置类型)“增加”新方法。而在实现某个接口时,无需从该接口继承(事实上,Go语言根本就不支持面向对象思想中的继承语法),只需要实现该接口要求的所有方法即可。任何类型都可以被Any类型引用。Any类型就是空接口,即interface{}。

 

  什么是结构体

 

  结构体(struct)是用户自定义的类型,它代表若干字段的集合,可以用于描述一个实体对象,类似java中的class,是golang面向对象编程的基础类型。

 

  结构体的概念在软件工程上旧的术语叫ADT(抽象数据类型:AbstractDataType)。在C++它也存在,并且名字也是struct,在面向对象的编程语言中,跟一个无方法的轻量级类一样。因为Go语言中没有类的概念,所以在Go中结构体有着更为重要的地位。

 

  如何定义一个结构体

 

  typeCoordinatestruct{

 

  X,Yfloat32

 

  }

 

  语法:typestruct{}

 

  上述代码定义个一个名为Coordinate的结构体,里面包括了两个float32的变量X,Y,该结构体可用于表示一个平面坐标。

 

  添加对象方法

 

  其他的经典面向对象语言,如java,C#,定义对象方法时,会包含在class的定义内,如

 

  publicclassCoordinate{

 

  publicfloatX{get;set;}

 

  publicfloatY{get;set;}

 

  //打印坐标

 

  publicvoidGetCoordinate(){

 

  Console.WriteLine("("+this.X+","+this.Y+")");

 

  }

 

  }

 

  在go语言中,对象方法在结构体定义的外部添加

 

  typeCoordinatestruct{

 

  X,Yfloat32

 

  }

 

  //打印坐标

 

  func(coo*Coordinate)GetCoordinate(){

 

  fmt.Printf("(%.2f,%.2f)\n",coo.X,coo.Y)

 

  return

 

  }

 

  其中,func关键字后面的(coo*Coordinate),表示该函数传入一个指向Coordinate的指针,可通过指针变量coo来操作结构体的值。

 

  几种结构体初始化

 

  一、按原始字段顺序通过创建结构体

 

  packagemain

 

  import(

 

  "fmt"

 

  )

 

  funcmain(){

 

  p0:=Coordinate{1,2}

 

  p0.GetCoordinate()

 

  }

 

  输出:(1.00,2.00),其中X=1,Y=2

 

  二、按照自定义字段顺序进行初始化

 

  packagemain

 

  import(

 

  "fmt"

 

  )

 

  funcmain(){

 

  p0:=Coordinate{Y:1,X:2}

 

  p0.GetCoordinate()

 

  }

 

  输出:(2.00,1.00),其中X=2,Y=1

 

  三、通过new函数创建

 

  packagemain

 

  import(

 

  "fmt"

 

  )

 

  funcmain(){

 

  //给该结构体p2变量分配内存,它返回指向已分配内存的指针

 

  p0:=new(Coordinate)

 

  p0.X=1

 

  p0.Y=2

 

  p0.GetCoordinate()

 

  }

 

  输出:(1.00,2.00),其中X=1,Y=2

 

  其中p0:=new(Coordinate)等价于以下写法

 

  p3:=&Coordinate{X:1,Y:2}

 

  p3:=&Coordinate{1,2}

 

  比较三种创建方式

 

  其中,种与第二种,p0均为一个类型为Coordinate的实例,而第三种p0为一个指向Coordinate的指针,相当于varp0*Coordinate=new(Coordinate)

 

  一般在进行例如typeTstruct{a,bint}的结构体定义之后

 

  习惯使用t:=new(T)给该结构体变量分配内存,它返回指向已分配内存的指针。变量t是一个指向T的指针,此时结构体字段的值是它们所属类型的零值。

 

  声明vartT也会给t分配内存,并零值化内存,但是这个时候t是类型T。在这两种方式中,t通常被称做类型T的一个实例(instance)或对象(Object)。vart*T=new(T)等价于t:=new(T)。

 

  通过代码分析以上结论

 

  packagemain

 

  import(

 

  "fmt"

 

  )

 

  funcmain(){

 

  p0:=Coordinate{1,2}

 

  //给该结构体p2变量分配内存,它返回指向已分配内存的指针

 

  p2:=new(Coordinate)

 

  p2.X=1

 

  p2.Y=2

 

  p3:=&Coordinate{X:1,Y:2}

 

  p4:=&Coordinate{1,2}

 

  fmt.Println("-------输出p0-------")

 

  fmt.Printf("%v\n%T\n",p0,p0)

 

  fmt.Println("-------输出p2-------")

 

  fmt.Printf("%v\n%T\n",p2,p2)

 

  fmt.Println("-------输出p3-------")

 

  fmt.Printf("%v\n%T\n",p3,p3)

 

  fmt.Println("-------输出p4-------")

 

  fmt.Printf("%v\n%T\n",p4,p4)

 

  }

 

  输出:

 

  -------输出p0-------

 

  {12}

 

  Coordinate

 

  -------输出p2-------

 

  &{12}

 

  *Coordinate

 

  -------输出p3-------

 

  &{12}

 

  *Coordinate

 

  -------输出p4-------

 

  &{12}

 

  *Coordinate

 

  可以看出来,p2,p3,p4均为一个指针变量

 

  添加值拷贝的对象方法

 

  刚才说到了,添加一个对象方法,可以通过func(t*T)functionname()来创建,其中t为一个指针变量。我们也可以通过值拷贝的方式,添加一个对象方法,语法为func(tT)functionname()

 

  packagemain

 

  import(

 

  "fmt"

 

  )

 

  typeCoordinatestruct{

 

  X,Yfloat32

 

  }

 

  func(coo*Coordinate)GetCoordinate(){

 

  fmt.Printf("(%.2f,%.2f)\n",coo.X,coo.Y)

 

  return

 

  }

 

  //值拷贝对象方法

 

  func(cooCoordinate)SetPosition01(afloat32,bfloat32){

 

  coo.X=a

 

  coo.Y=b

 

  }

 

  //指针变量对象方法

 

  func(coo*Coordinate)SetPosition02(afloat32,bfloat32){

 

  coo.X=a

 

  coo.Y=b

 

  }

 

  funcmain(){

 

  p0:=Coordinate{1,2}

 

  fmt.Print("SetPosition02调用前:")

 

  p0.GetCoordinate()

 

  p0.SetPosition02(0,0)

 

  fmt.Print("SetPosition02调用后:")

 

  p0.GetCoordinate()

 

  }

 

  输出:

 

  SetPosition01调用前:(1.00,2.00)

 

  SetPosition01调用后:(1.00,2.00)

 

  SetPosition02调用前:(1.00,2.00)

 

  SetPosition02调用后:(0.00,0.00)

 

  从程序输出中可以看出,调用SetPosition01方法,发生了值拷贝,即使在方法内改变了coo的值,外部的p0的值没有被改变。而SetPosition02方法中,coo为指向p0地址的指针,由于是通过指针变量修改了X,Y的值,所以调用完毕后,外部p0的值会被修改为(0,0)

 

  匿名结构体

 

  packagemain

 

  import(

 

  "fmt"

 

  )

 

  funcmain(){

 

  p_3d:=struct{

 

  X,Y,Zfloat32

 

  }{1,2,3}

 

  fmt.Println("-------输出p_3d-------")

 

  fmt.Printf("%v\n%T\n",p_3d,p_3d)

 

  }

 

  输出:

 

  -------输出p_3d-------

 

  {123}

 

  struct{Xfloat32;Yfloat32;Zfloat32}

 

  p_3d为一个包含X,Y,Z三个变量的匿名结构体

 

  golang构造函数?

 

  在Go语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以NewXXX来命名,表示“构造函数”:

 

  这一切非常自然,开发者也不需要分析在使用了new之后到底背后发生了多少事情。在Go语言中,一切要发生的事情都直接可以看到。

 

  funcNewRect(x,y,width,heightfloat64)*Rect{

 

  return&Rect{x,y,width,height}

 

  }

 

  变量、方法可见性

 

  Go语言对关键字的增加非常吝啬,其中没有private、protected、public这样的关键字。要使某个符号对其他包(package)可见(即可以访问),需要将该符号定义为以大写字母开头,如:

 

  typeRectstruct{

 

  X,Yfloat64

 

  Width,Heightfloat64

 

  }

 

  这样,Rect类型的成员变量就全部被导出了,可以被所有其他引用了Rect所在包的代码访问到。成员方法的可访问性遵循同样的规则,例如:

 

  func(r*Rect)area()float64{

 

  returnr.Width*r.Height

 

  }

 

  这样,Rect的area()方法只能在该类型所在的包内使用。

 

  需要注意的一点是,Go语言中符号的可访问性是包一级的而不是类型一级的。在上面的例子中,尽管area()是Rect的内部方法,但同一个包中的其他类型也都可以访问到它。这样的可访问性控制很粗旷,很特别,但是非常实用。如果Go语言符号的可访问性是类型一级的,少不了还要加上friend这样的关键字,以表示两个类是朋友关系,可以访问彼此的私有成员。

文中图片素材来源网络,如有侵权请联系删除
来源:北京兄弟连IT
热门课程 全部课程

热门动态

申请免费试听

只要一个电话

我们为您免费回电

立即申请