面向对象之结构体2

创建结构体变量和访问结构体字段

1)方式1-直接声明

案例演示:var person Person

2)方式2-{}

案例演示:var person Person = Person{}

func main() {
  p2 := Person{}
  p2.Name = "tom"
  p2.Age = 18
  fmt.Println(p2)
}


3)方式3-&

案例:var person *Person = new(Person)

func main() {
  //方式3
  var p3 *Person = new(Person)
  //因为p3是一个指针,因此标准的给字段赋值的方式
  //(*p3).Name = "smith" 也可以这样写 p3.Name = "smith"
  //原因是 go的设计者 为了程序员使用方便在底层会对 p3.Name = "smith" 进行处理
  //会给p3 加上 取值运算 (*p3).Name = "smith"
  (*p3).Name = "smith"
  p3.Name = "john"
  (*p3).Age = 30
  p3.Age = 100
  fmt.Println(*p3)
}

4)方式4-{}

案例:var person *Person = &Person{}

func main() {
  //方式4
  //下面的语句,也可以直接给字符赋值
  //var person *Person = &Person{"mary", 60}
  var person *Person = &Person{}
  //因为person 是一个指针,因此标准的访问字段的方法
  // (*person).Name = "scott"
  //go的设计者为了程序员使用方便,也可以直接写成 person.Name = "scott"
  //原因和上面一样,底层会做一些处理
  (*person).Name = "scott"
  person.Name = "scott~"
  (*person).Age = 88
  person.Age = 10
  fmt.Println(*person)
}


说明:
(1) 第3种和第4种方式返回的是 结构体指针

(2)结构体指针访问字段的标准方式应该是:(*结构体指针).字段名,比如:(*person).Name = "tom"

(3) 但go做了一个简化,也支持 结构体指针.字段名,比如 person.Name = "tom"。更加符合程序员使用的习惯,go编译器底层对 person.Name 做了转化 (*person).Name

 

结构体内存分配机制: 

基本说明:

变量总是存在内存中的,那么结构体变量在内存中究竟是怎么样存在的?

看下面代码,并分析原因:

下面一段代码,会输出什么信息:

var p1 Person
p1.Age = 10
p1.Name = "小明"
var p2 *Person = &p1

fmt.Println((*p2).Age) //p1的Age 10
fmt.Println(p2.Age) //p1的Age 10
p2.Name = "tom~"
fmt.Printf("p2.Name=%v p1.Name=%v \n", p2.Name, p1.Name) //tom~
fmt.Printf("p2.Name=%v p1.Name=%v \n", (*p2).Name, p1.Name) //tom~

输出的结果:

在内存中的示意图:

看下面的代码,并分析原因

var p1 Person
p1.Age =10
p1.Name = "小明"
var p2 *Person = &p1

fmt.Println(*p2.Age) //能不能这样写?

不对,因为.的运算符优先级比*高,所以先算它以后才会算*。 所以必须加括号(*p2).Age

 

结构体使用细节:

1)结构体的所有字段在内存中是连续的

案例:

type Rect struct {
  leftUp, rightDown Point
}

type Rect2 struct {
  leftUp, rightDown *Point
}

func main() {

  r1 := Rect{Point{1,2}, Point{3,4}}
  //r1有四个整数int,在内存中是连续分布
  //打印地址
  fmt.Printf("r1.leftUp.x 的地址=%p r1.leftUp.y 的地址=%p r1.rightDown.x 的地址=%p r1.rightDown.y 的地址=%p \n",
  &r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)

  //r2有两个 *Point类型,这两个*Point类型的本身地址也是连续的
  //但是他们指向的地址不一定是连续的
  r2 := Rect2{&Point{10,20}, &Point{30,40}}

  //打印地址
  fmt.Printf("r2.leftUp 本身地址=%p r2.rightDown 本身地址=%p \n",
  &r2.leftUp, &r2.rightDown)
  //他们指向的地址不一定是连续... 这个要看系统在运行时是怎么分配的
  fmt.Printf("r2.leftUp 指向地址=%p r2.rightDown 指向地址=%p \n",
  r2.leftUp, r2.rightDown)
}

在内存中的示意图:

2)结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)


type A struct {
  Num int
}

type B struct {
  Num int
}

func main() {

  var a A
  var b B
  a = A(b) //可以转换,但是有要求,结构体的字段要完全一样(名字,个数和类型)
  fmt.Println(a,b)

}


3)结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转

type Student struct {
  Name string
  Age int
}

type Stu Student

  func main() {
  var stu1 Student
  var stu2 Stu
  stu2 = stu1   //正确吗? 错误的,可以这样修改stu2 = Stu(stu1)
  fmt.Println(stu1,stu2)
}

type integer int

  func main() {
  var i integer = 10
  var j int = 20
  j = i   //正确吗? 错误的,因为golang认为integer是一种新的数据类型了,不能直接赋值,必须转换 j = int(i)
  fmt.Println(i,j)
}


4)struct 的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列号和反序列化。


案例演示:

package main
import (
  "fmt"
  "encoding/json"
)

type Monster struct {
  Name string `json:"name"` // `json:"name"` 就是 struct tag
  Age int `json:"age"`
  Skill string `json:"skill"`
}

func main() {

  //1.创建一个Monster变量
  monster := Monster{"牛魔王", 500, "芭蕉扇~"}

  //2.将monster变量序列化为 json格式的字串
  // json.Marshal 函数中使用反射,这个讲解反射时再详细介绍
  jsonStr,err := json.Marshal(monster)
  if err != nil {
    fmt.Println("json 处理错误", err)
  }
  fmt.Println("jsonStr", string(jsonStr))
}

 

转载于:https://www.cnblogs.com/green-frog-2019/p/11408097.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值