Go语言学习笔记--类型、指针、面向对象

Mervyn1205 · · 2918 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Go 有指针,但是没有指针运算。你不能用指针变量遍历字符串的各个字节.
通过类型作为前缀来定义一个指针’*’:var p *int。现在p 是一个指向整数值的指针。
所有新定义的变量都被赋值为其类型的零值,而指针也一样。一个新定义的或者没有任何指向的指针,有值nil。在其他语言中,这经常被叫做空(NULL)指针,在Go 中就是nil。让指针指向某些内容,可以使用取址操作符(&)

如果一个method的receiver是*T,你可以在一个T类型的实例变量V上面调用这个method,而不需要&V去调用这个method
类似的
如果一个method的receiver是T,你可以在一个*T类型的变量P上面调用这个method,而不需要 *P去调用这个method

例1:
package main
import "fmt"
func main(){
    var p *int
    fmt.Printf("%v\n", p)
    var i int
    p = &i
    fmt.Printf("%v\n", p)
}

输出结果:
<nil>
0x18600118

例2:
package main
import "fmt"
func main(){
    var p *int
    var i int
    p = &i
    *p = 8
    fmt.Printf("%v\n", *p)
    fmt.Printf("%v\n", i)
}
输出结果:
8
8

因为Go没有指针运算,所以如果这样写:*p++,它表示(*p)++:首先获取指针指向的值,然后对这个值加一。

Go 有两个内存分配原语,new 和make

1、用new 分配内存
它返回了一个指针,指向新分配的类型T 的零值。

2、用make 分配内存
内建函数make(T, args) 与new(T) 有着不同的功能。它只能创建slice,map和channel,并且返回一个有初始值(非零)的T 类型,而不是*T。

new(T) 返回*T 指向一个零值T
make(T) 返回初始化后的T

定义自己的类型
Go 允许定义新的类型,通过保留字type 实现:
type foo int
创建了一个新的类型foo 作用跟int 一样。创建更加复杂的类型需要用到struct 保留字。

例:
package main
import "fmt"
type NameAge struct {
    name string        //不导出
    age int            //不导出
}
func main(){
    a := new(NameAge)
    a.name = "Pete"
    a.age = 42
    fmt.Printf("%v\n", a)
    fmt.Printf("%v\n", a.name)
}
输出结果:
&{Pete 42}
Pete

注意struct{}中首字母大写的字段可以被导出,也就是说,在其他包中可以进行读写。字段名以小写字幕开头是当前包的私有的。

例2:
type person struct {
    name string
    age int
}
var P person  // P现在就是person类型的变量了

P.name = "Astaxie"  // 赋值"Astaxie"给P的name属性.
P.age = 25          // 赋值"25"给变量P的age属性
fmt.Printf("The person's name is %s", P.name)  // 访问P的name属性.

除了上面这种P的声明使用之外,还有两种声明使用方式

  • 1.按照顺序提供初始化值

    P := person{"Tom", 25}
    
  • 2.通过field:value的方式初始化,这样可以任意顺序

    P := person{age:24, name:"Tom"}

例3:
package main
import "fmt"

// 声明一个新的类型
type person struct {
    name string
    age int
}

// 比较两个人的年龄,返回年龄大的那个人,并且返回年龄差
// 注意:struct也是传值的
func Older(p1, p2 person) (person, int) {
    if p1.age>p2.age {  // 比较p1和p2这两个人的年龄
        return p1, p1.age-p2.age
    }
    return p2, p2.age-p1.age
}

func main() {
    var tom person
    // 赋值初始化
    tom.name, tom.age = "Tom", 18

    // 两个字段都写清楚的初始化
    bob := person{age:25, name:"Bob"}

    // 按照struct定义顺序初始化值
    paul := person{"Paul", 43}

    tb_Older, tb_diff := Older(tom, bob)
    tp_Older, tp_diff := Older(tom, paul)
    bp_Older, bp_diff := Older(bob, paul)

    fmt.Printf("Of %s and %s, %s is older by %d years\n",
        tom.name, bob.name, tb_Older.name, tb_diff)

    fmt.Printf("Of %s and %s, %s is older by %d years\n",
        tom.name, paul.name, tp_Older.name, tp_diff)

    fmt.Printf("Of %s and %s, %s is older by %d years\n",
        bob.name, paul.name, bp_Older.name, bp_diff)
}
输出结果:
Of Tom and Bob, Bob is older by 7 years
Of Tom and Paul, Paul is older by 25 years
Of Bob and Paul, Paul is older by 18 years

struct的匿名字段

我们上面介绍了如何定义一个struct,定义的时候是字段名与其类型一一对应,实际上Go支持只提供类型,而不写字段名的方式,也就是匿名字段,也称为嵌入字段。当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。

例:
package main
import "fmt"

type Human struct {
    name string
    age int
    weight int
}

type Student struct {
    Human  // 匿名字段,那么默认Student就包含了Human的所有字段
    speciality string
}

func main() {
    // 我们初始化一个学生
    mark := Student{Human{"Mark", 25, 120}, "Computer Science"}

    // 我们访问相应的字段
    fmt.Println("His name is ", mark.name)
    fmt.Println("His age is ", mark.age)
    fmt.Println("His weight is ", mark.weight)
    fmt.Println("His speciality is ", mark.speciality)
    // 修改对应的备注信息
    mark.speciality = "AI"
    fmt.Println("Mark changed his speciality")
    fmt.Println("His speciality is ", mark.speciality)
    // 修改他的年龄信息
    fmt.Println("Mark become old")
    mark.age = 46
    fmt.Println("His age is", mark.age)
    // 修改他的体重信息
    fmt.Println("Mark is not an athlet anymore")
    mark.weight += 60
    fmt.Println("His weight is", mark.weight)
}
输出结果:
His name is  Mark
His age is  25
His weight is  120
His speciality is  Computer Science
Mark changed his speciality
His speciality is  AI
Mark become old
His age is 46
Mark is not an athlet anymore
His weight is 180

方法
可以对新定义的类型创先函数以便操作,可以通过两种途径:
1. 创建一个函数接受这个类型的参数。
    func doSomething(in1 *NameAge, in2 int) { /* ... */ }
    (你可能已经猜到了)这是函数调用。
2. 创建一个工作在这个类型上的函数(参阅在3.1 中定义的接收方):
    func (in1 *NameAge) doSomething(in2 int) { /* ... */ }
    这是方法调用,可以类似这样使用:
    var n *NameAge
    n.doSomething(2)

例1:
package main
import "fmt"
type NameAge struct {
    name string
    age int
}
func main(){
    a := new(NameAge)
    a.name = "Pete"
    a.age = 42
    doSomething(a, 3)
}
func doSomething(in1 *NameAge, in2 int){
    fmt.Printf("%v\n", in1.name)
    fmt.Printf("%d\n", in2)
}
输出结果:
Pete
3

例2:
package main
import "fmt"
type NameAge struct {
    name string
    age int
}
func main(){
    a := new(NameAge)
    a.name = "Pete"
    a.age = 42
    a.doSomething(333)
}
func (in1 *NameAge) doSomething(in2 int){
    fmt.Printf("%v\n", in1.name)
    fmt.Printf("%d\n", in2)
}
输出结果:
Pete
333

类型转换
有时需要将一个类型转换为另一个类型。在Go 中可以做到,不过有一些规则。

注意Go 的字符串是UTF-8 编码的,一些字符可能是1、2、3 或者4 个字节结尾。

例:
package main
import "fmt"
func main(){
    mystring := "hello world"
    byteslice := []byte(mystring)
    for _, val := range byteslice {
        fmt.Printf("%v\n", val)
    }
}
输出结果:
104
101
108
108
111
32
119
111
114
108
100

例2
package main
import "fmt"
func main(){
    mystring := "hello world"
    byteslice := []rune(mystring)
    for _, val := range byteslice {
        fmt.Printf("%c\n", val)
    }
}
输出结果:
h
e
l
l
o

w
o
r
l
d
对于数值,定义了下面的转换:
• 将整数转换到指定的(bit)长度:uint8(int);
• 从浮点数到整数:int(float32)。这会截断浮点数的小数部分;
• 其他的类似:float32(int)。
例:
package main
import "fmt"
func main(){
    myfloat := 123.72
    myint := int(myfloat)
    fmt.Printf("%.3f\n", myfloat)
    fmt.Printf("%d\n", myint)
}
输出结果:
123.720
123

用户定义类型的转换
如何在自定义类型之间进行转换?这里创建了两个类型Foo 和Bar,而Bar 是Foo 的一个别名:
type foo struct { int }   匿名字段
type bar foo   bar 是foo 的别名
然后:
var b bar = bar{1}   声明b 为bar 类型
var f foo = b   赋值b 到f
最后一行会引起错误:
cannot use b (type bar) as type foo in assignment(不能使用b(类型bar)作为类型foo 赋值)
这可以通过转换来修复:
var f foo = foo(b)
注意转换那些字段不一致的结构是相当困难的。同时注意,转换b 到int 同样会出错;整数与有整数字段的结构并不一样。

例:
package main
import "fmt"
type foo struct { int }        //匿名字段
type bar foo
func main(){
    b := bar{1}                //声明b 为bar 类型,并赋初值
    var f foo = b              
    //上面赋值b 到f,这会引起一行错误cannot use b (type bar) as type foo in assignment(不能使用b(类型bar)作为类型foo 赋值)
    var f foo = foo(b)        //这个是可以的
    fmt.Printf("%d\n", f)
}
输出结果:
{1}

注意:转换b 到int 同样会出错;整数与有整数字段的结构并不一样。









有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:Mervyn1205

查看原文:Go语言学习笔记--类型、指针、面向对象

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2918 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传