Golang 结构体【防备忘】

Golang 结构体

  1. 结构体字段
结构体中的那些数据称为字段
字段可以是任何类型,也可以是结构体本身,甚至是函数和接口,都可以
  1. 结构体定义
整体来说,与C语言的定义方式相同
type struct_name struct{
	...
}
结构体的字段可以是任何类型,甚至是结构体本身,也可以是函数或者接口
与C++中的类,很相似,但是go没有类的概念

type ueueQ struct{
	a int
	b float32
}
一、
var temp ueueQ
temp = ueueQ{100, 99.9}
二、
temp := ueueQ{100, 99.9}
temp := ueueQ{a:100, b:99.9}
temp := ueueQ{b:99.9}   
三、
var temp *ueueQ
temp = new(ueueQ)
temp.a = 100
temp.b = 99.9
(*temp).a = 100  //也是合法的
四、
temp := &ueueQ{100,99.9}
temp即为ueueQ类型的指针
  1. 用结构体初始化另一个结构体
type ueueQ struct{
	a int
	b float32
}

type xml ueueQ  //给ueueQ起别名*********

func main(){
	var class1 = ueueQ{100, 99.9}
	class2 := xml{10, 9.9}
	var class3 ueueQ = calss2   //这样是非法的,type xml 不能赋值给 type ueueQ
	var class3 = xml(class2)   //合法
	var class3 = ueueQ(class1)  //合法
}
  1. 结构体工厂
这个东西是个什么意思呢?
比如C++中的类有构造函数,可以对对象进行初始化,但是go的结构体,可以使用结构体工厂“制造结构体”
type ueueQ struct{
	a int
	b int
}

func (w *ueueQ)NewQueue(_a,_b int) *ueueQ,e{
	w.a=_a
	w.b=_b
	return w,true
}

tnlp,val :=NewQueue(7,3)
这样在包外构建该类型结构体的时候,也很优雅了
  1. make与new
切片、map、channel   可以用make
结构体可以用new,不能用make
  1. 自定义包中的结构体如何让外包使用
一、
type ExpStruct struct {
    Mi1 int
    Mf1 float32
}
此结构体,外包既能用ExpStruct,也能访问结构体中的数据,因为都是大写的
二、结构体导出了,字段未导出
type ExpStruct struct {
    mi1 int
    mf1 float32
}
此结构体,外包只能使用ExpStruct,而不能访问结构体内的数据,因为是小写,即私有
那么如何在包外使用呢?就是oop上的setter和getter方法了
func (p *ExpStruct ) SetFirstName(newName float32) {   //间接的实现
    p.mf1 = newName
}
  1. 带标签的结构体
  2. 匿名字段与内嵌结构体
结构体可以包含一个或多个 匿名(或内嵌)字段
即这些字段没有显式的名字,只有字段的类型是必须的,此时类型就是字段的名字
匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体
package main

import "fmt"

type innerS struct {
    in1 int
    in2 int
}

type outerS struct {
    b    int
    c    float32
    int  // anonymous field
    innerS //anonymous field
}

func main() {
    outer := new(outerS)
    outer.b = 6
    outer.c = 7.5
    outer.int = 60
    outer.in1 = 5
    outer.in2 = 10

    fmt.Printf("outer.b is: %d\n", outer.b)
    fmt.Printf("outer.c is: %f\n", outer.c)
    fmt.Printf("outer.int is: %d\n", outer.int)
    fmt.Printf("outer.in1 is: %d\n", outer.in1)
    fmt.Printf("outer.in2 is: %d\n", outer.in2)

    // 使用结构体字面量
    outer2 := outerS{6, 7.5, 60, innerS{5, 10}}
    fmt.Println("outer2 is:", outer2)
}
可以得出结论:
在一个结构体中,对于每一种数据类型只能有一个匿名字段。【因为类型代表字段名字】
  1. 内嵌结构体的命名冲突问题
一、不同级命名冲突
type temp struct{
	a int
}
type xnl struct{
	a int
	val temp
}
var obj xnl
obj.a表示的xnl中的a
obj.val.a表示的是继承的temp中的a
二、同一级命令冲突
type temp1 struct{
	a int
}
type temp2 struct{
	a int
}
type xnl struct{
	x1 temp1
	x2 temp2
}
var obj xnl
obj.a会发生错误,因为它不知道是哪一个结构体里面的a
只能obj.temp1.a或者obj.temp2.a这样使用
  1. 方法Method
就是指定接收者(类型),只能由该接收者才能调用的函数,称为方法。【非正式】
type ueueQ struct{
	x1 int
	...
}func (this *ueueQ) Add(a,b int) int{...}
②接收者不同的话,方法名是可以相同的
func (this *ueueQ) Add(a,b int) int{...}
func (this *ueueQslef) Add(a,b int) int{...}
③用type xml ueueQ 起别名
别名是不允许和原结构体有相同命名的方法的
④假如在函数体内不使用接收者的话,可以用这种形式
func (_ *ueueQ) Add(a,b int) int{...}
⑤非结构体接收者也能写方法
func (arr []int) Add(a,b int) int{...}
  1. 方法的注意
***注意:方法定义必须要和结构体在同一个包中,但是可以不是同一个源文件
但是,如果我写一个外包定义的结构体的方法怎么办呢?
**可以为这个外部包type一个别名,或者将其放在一个结构体里面做匿名字段**
  1. 函数与方法的区别
方法没有和数据定义(结构体)混在一起:它们是正交的类型;表示(数据)和行为(方法)是独立的。
这个好好体会下,这也就是为什么go没有this指针,仍旧能判断出是那对象在调用该函数的原因。
  1. 指针与值作为接收者的区别
type ueueQ struct {
	a int
}
func (th *ueueQ) addPiont(tm int) {  //接收者为指针
	th.a += tm
}
func (th ueueQ) addnopoint(tm int) {  //接收者为值
	th.a += tm
}

var obj1, obj2 ueueQ
var objP = new(ueueQ)
obj2.a = 1
obj1.a = 1
objP = &obj2

接收者虽然为指针,但并不一定只能有该类型的指针去调用,也能由该类型的值调用,go会将值转为指针
接收者虽然为值,但并不一定只能有该类型的值去调用,也能由该类型的指针调用,go会解引用
Go语言会在“背后”转换

但需要注意的是:
若接收者为指针,无论值还是指针调用它,都会改变自己的字段里面的内容
若接收者为值,无论值还是指针调用它,都不会改变自己字段的内容,只是拷贝
就是只看接收者类型,不用看调用者的类型,可以做试验验证下

  1. 内嵌类型的方法与继承
如果方法继承了,机制是如何的?
type baba struct {
	b int
}

func (th *baba) add() {
	fmt.Println("调用了爸爸")
}

type son struct {
	s int
	baba
}

func (th *son) add() {     //覆写了方法
	fmt.Println("调用了儿子")
}

func main() {
	var xml son
	xml.add()
	xml.baba.add()
}

输出:
调用了儿子
调用了爸爸

如果子类中没有和父类同名的方法,则直接可以通过子类实例调用父类方法
  1. 如何在类型中嵌入功能
子类继承父类的功能,该如何去使用它?
一、聚合(或组合):包含一个所需功能类型的具名字段
type Log struct {
    msg string
}
func (th *Log)oop(){...}

type Customer struct {
    Name string
    log  *Log   //字段直接具名,不在匿名
}
func (th *Customer)Log() *Log{  return th.Log}

var obj Customer
obj.Log().oop()   //这样间接的调用

子类Customer在使用父类Log功能的时候,不是直接调用父类的方法,而是通过具名字段

二、内嵌:内嵌(匿名地)所需功能类型
type Log struct {
    msg string
}
func (th *Log)oop(){...}

type Customer struct {
    Name string
    Log
}

var obj Customer
obj.Log.oop()   //通过匿名字段调用父类方法

  1. 多重继承
一个继承多个,不在赘述
  1. 类型的 String() 方法和格式化描述符
一个结构体,如果该类型类型定义了 String() 方法。
那么,fmt.Print()、fmt.Printf()、fmt.Println()会自动的使用String()方法

type ueueQ struct {
    a int
    b int
}
func ( th *ueueQ)String() string{
	return "(" + strconv.Itoa(tn.a) + "/" + strconv.Itoa(tn.b) + ")"
}

func main(){
	v := new(TwoInts)
    v.a = 12
    v.b = 10
    fmt.Printf("two1 is: %v\n", v)
    fmt.Println("two1 is:", v)
    fmt.Printf("two1 is: %T\n", v)   格式化描述符 %T 会给出类型的完全规格
    fmt.Printf("two1 is: %#v\n", v)  %#v 会给出实例的完整输出
	fmt.println(v)   //这里会直接调用ueueQ的String()方法
}
输出:
two1 is: (12/10)
two1 is: (12/10)
two1 is: *main.TwoInts        注意下
two1 is: &main.TwoInts{a:12, b:10}     注意下
不要再String()方法里面,直接打印该类型,
type TT float64

func (t TT) String() string {
    return fmt.Sprintf("%v", t)   //这样会造成递归调用,无限吃内存
}
t. String()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋山刀名鱼丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值