结构体后续&指针

指针

变量和内存地址

  • 每个变量都有内存地址,可以通过变量来操作对应的内存
func varMem() {
   var a int32 = 100
   fmt.Printf("addr %p\n",&a)
}

func pointMem() {
   var b int32
   b = 32
   var a *int32
   fmt.Printf("addr of a:%v\ntype of a %T\n",a,a)  //取出a的地址和类型
   a = &b
   fmt.Printf("a %d addr:%p\ntype of a:%T\nb %d addr %p\n",*a,&a,a,b,&b)
}

指针的定义与取值

  • &: 取地址
  • *: 取指针对应的值
  • 充分判断指针为nil的情况
func pointMem1()  {
   var a *int
   b := 200
   a = &b
   *a = 400
   fmt.Printf("a=%v,adda=%p,type a=%#T\nb=%v,addb=%p,type b=%#T\n",*a,a,a,b,&b)
}
func pointMem2()  {
   var a *int
   var b int = 100
   if a == nil {
      a = &b
   }
   fmt.Printf("addr a=%p,value a=%d\naddr b=%p,value b=%d\n",a,*a,&b,b)
}
  • 小例子

猜输出

func modify(a int) {
   a = 1000
}

func modify2(a *int) {
   fmt.Printf("address of a: %p\n",&a)
   *a = 1000
}

func main() {
   var b int = 100
   modify(b)
   fmt.Printf("b=%d\n",b)
   var p *int = &b
   fmt.Printf("address of p: %p\n",&p)
   modify2(&b)
   fmt.Printf("b=%d\n",b)
}

指针变量传参

  • 修改数组
func modifyArray(arr *[3]int) {
   //经过修改之后的arr,成了引用类型而不是值类型,因为其内存地址保持一致
   fmt.Printf("2. arr=%#v,address=%p\n",*arr,&arr)
   (*arr)[0] = 90
   fmt.Printf("3. arr=%#v,address=%p\n",*arr,&arr)
}

func main() {
   a := [3]int{89,90,91}
   modifyArray(&a)
   fmt.Printf("1. arr=%#v,addreess=%p\n",a,&a)
}
  • 切片传参: 切片是引用类型,底层是指针,所以可以直接修改
func modifySlice(a []int) {
   fmt.Printf("2.slice=%v,address=%p\n",a,&a)
   a[0] = 90
   fmt.Printf("3.slice=%v,address=%p\n",a,&a)
}

func main() {
   a := []int{89,90,91}
   modifySlice(a)
   fmt.Printf("1. silce=%#v,address=%p\n",a,&a)
}

指针类型

  • make用来分配引用类型的内存,例如:map,channel,slice
  • new用来分配除引用类型的所有其他类型的内存,例如: int,数组等
func newPoints() {
   var p *int = new(int)
   fmt.Printf("p=%v,address=%p\n",p,&p)
   *p = 100
   fmt.Printf("p=%v,address=%p\n",*p,&p)
}

type User struct {
   Name     string
   Age      int
}

func StructPoint() {
   var pUser *User = new(User)
   pUser.Name = "alex"
   pUser.Age = 19
   fmt.Printf("user:%v\n",*pUser)
}

func newSlice() {
   var p *[]int = new([]int)
   *p = make([]int,10)
   (*p)[0] = 100
   fmt.Printf("p=%v,address=%p\n",*p,&p)
}

func newMap() {
   var p *map[string]int = new(map[string]int)
   *p = make(map[string]int)
   (*p)["key1"] = 100
   (*p)["key2"] = 200
   fmt.Printf("p=%v,address=%p\n",*p,&p)
}

func main() {
   newPoints()
   StructPoint()
   newSlice()
   newMap()
}

值拷贝和引用拷贝

  • 值拷贝就是将拷贝对象重新开辟内存空间,然后将拷贝值放入新的内存空间
  • 引用拷贝就是拷贝内存地址,指向同一个值
func valueCopy() {
   a := 10
   b := a
   fmt.Printf("a=%d,address a=%p,b=%d,address b=%p\n",a,&a,b,&b)
   c := 10
   d := &c
   fmt.Printf("c=%d,address c=%p,d=%d,address d=%p\n",c,&c,*d,d)
}

面向对象编程

方法的定义

  • 跟其他语言不一样,go的方法采用另外一种方式实现
  • Go的方法是在函数前面加上一个接受者,这样编译器就知道这个方法属于哪个类型了
type Students struct {
   Name string
   Age  int
}

//定义了值类型为Students的方法
func (s Students) GetName() string{
   return s.Name
}

//修改需要指针类型的方法
func (s *Students) SetName(name string) {
   s.Name = name
}

func main() {
   var s1 Students  = Students{
      Name:"s1",
      Age:12,
   }
   name := s1.GetName()
   fmt.Printf("name=%s\n",name)

   //修改name
   s1.SetName("s2")
   name = s1.GetName()
   fmt.Printf("name=%s\n",name)
}

  • 可以为当前包内的任何类型增加方法
//定义一个integer类型
type Integer int64

func (i Integer) Print()  {
   fmt.Printf("i=%d\n",i)
}

func (i *Integer) Set(b int64) {
   *i = Integer(b)
}

func main() {
   var a Integer
   a = 1000
   fmt.Printf("a=%d\n",a)
   var b int64 = 500
   a = Integer(b)
   fmt.Printf("a=%d\n",a)
   a.Print()
   a.Set(10000)
   a.Print()
}
  • 函数不属于任何类型,方法属于特定类型
  • 指针类型作为接受者
  • 值类型和指针类型作为接受者的区别
    • 指针类型的话可以修改
    • 值类型只能作为只读的
    • 一般将指针类型作为读写的接受者即可
//定义一个integer类型
type Integer int64

func (i *Integer) Print()  {
   fmt.Printf("i=%d\n",*i)
}

func (i *Integer) Set(b int64) {
   *i = Integer(b)
}

func main() {
   var a Integer
   a = 1000
   fmt.Printf("a=%d\n",a)
   var b int64 = 500
   a = Integer(b)
   fmt.Printf("a=%d\n",a)
   a.Print()
   a.Set(10000)
   a.Print()
}

什么时候使用指针类型

  • 需要修改接受者中的值的时候
  • 接受者是大对象的时候,拷贝副本代价比较大
  • 一般来时,通常使用指针类型做为接受者
type Users struct {
   s1 [100000000]int64
   s2 [100000000]int64
   s3 [100000000]int64
   s4 [100000000]int64
}

func (u *Users) SetValue() {
   for i :=0;i<len(u.s1);i++ {
      u.s1[i] = 1
      u.s2[i] = 1
      u.s3[i] = 1
      u.s4[i] = 1
   }
}

func main() {
   var u *Users = new(Users)
   start := time.Now().UnixNano()
   u.SetValue()
   end := time.Now().UnixNano()
   fmt.Printf("总耗时:%v ms\n",(end-start)/1000000)
}

方法的继承

  • 结构体继承
  • 方法继承
type Animal struct {
   Name string
   Age  int
}

func (a *Animal) SetName(name string) {
   a.Name = name
}

func (a *Animal) SetAge(age int) {
   a.Age = age
}

func (a *Animal) Print() {
   fmt.Printf("a.name=%s a.age=%d\n",a.Name,a.Age)
}

type Brids struct {
   //继承父类,使用指针,初始化的时候也需要初始化父类
   *Animal
}

func (b *Brids) Fly() {
   fmt.Printf("name %s is flying \n",b.Name)
}

func main() {
   //继承父类,使用指针,初始化的时候也需要初始化父类
   var b *Brids = &Brids{
      &Animal{},
   }
   //继承了父类的方法
   b.SetName("birds")
   b.SetAge(12)
   //调用自己的方法
   b.Fly()
}
多重继承与冲突解决
  • 不推荐多重继承

结构体转成json(序列化)

type Animal struct {
   Name string
   Age  int
}

func (a *Animal) SetName(name string) {
   a.Name = name
}

func (a *Animal) SetAge(age int) {
   a.Age = age
}

func (a *Animal) Print() {
   fmt.Printf("a.name=%s a.age=%d\n",a.Name,a.Age)
}

type Brids struct {
   //继承父类,使用指针,初始化的时候也需要初始化父类
   *Animal
}

func (b *Brids) Fly() {
   fmt.Printf("name %s is flying \n",b.Name)
}

func main() {
   //继承父类,使用指针,初始化的时候也需要初始化父类
   var b *Brids = &Brids{
      &Animal{},
   }
   //继承了父类的方法
   b.SetName("birds")
   b.SetAge(12)
   //调用自己的方法
   b.Fly()
   // json序列化
   data,err := json.Marshal(b)
   fmt.Printf("marshal result:%s,error :%v\n",data,err)
   //反序列化:变成结构体
   var c Brids
   json.Unmarshal(data,&c)
   fmt.Printf("%#v\n",c.Animal)
}