结构体后续&指针
指针
变量和内存地址
- 每个变量都有内存地址,可以通过变量来操作对应的内存
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)
}