golang之结构体和方法

结构体的定义

结构体是将零个或者多个任意类型的命令变量组合在一起的聚合数据类型。
每个变量都叫做结构体的成员。

其实简单理解,Go语言的结构体struct和其他语言的类class有相等的地位,但是GO语言放弃了包括继承在内的大量面向对象的特性,只保留了组合这个基础的特性。
所有的Go语言类型除了指针类型外,都可以有自己的方法。

先通过一个下的例子理解struct。

package main

import "fmt"

type Student struct {
    Name  string
    Age   int
    Sex   string
    Score int
}

func main() {
    var stu Student
    stu.Name = "zhangsan"
    stu.Age = 12
    stu.Sex = "male"
    stu.Score = 90
    fmt.Printf("name:%s\n age:%d\n sex:%s\n score:%d\n", stu.Name, stu.Age, stu.Sex, stu.Score)

    fmt.Printf("%v\n", stu)
    fmt.Printf("%+v\n", stu)
    fmt.Printf("%#v\n", stu)
}

关于Go中的struct:

  1. 用于定义复杂的数据结构
  2. struct里面可以包含多个字段(属性),字段可以是任意类型
  3. struct类型可以定义方法(注意和函数的区别)
  4. struct类型是值类型
  5. struct类型可以嵌套
  6. Go语言没有class类型,只有struct类型

定义一个struct

struct声明:

type 标识符 struct {
field1 type
field2 type
}

例子:
type Student struct {
Name string
age int
}

struct中字段的访问,和其他语言一样使用“.”点这个符号
var stu Student
stu.Name = "tom"
stu.Age = 18

赋值的时候我们是通过stu.Name同样的我们访问的时候也是通过stu.Name

struct定义的三种形式

type Student struct {
Name string
age int
}
对于上面这个结构体,我们定义的三种方式有:

  1. var stu student
  2. var stu *Student = new(Student)
  3. var stu *Student = &Student

上面三种方法中,方法2和方法3的效果是一样的,返回的都是指向结构体的指针,访问的方式如下:
stu.Name,stu.Age
(*stu).Name,(*stu).Age而这种方法中可以换成上面的方法直接通过stu.Name访问
这里是go替我们做了转换了,当我们通过stu.Name访问访问的时候,go会先判断stu是值类型还是指针类型如果是指针类型,会替我们改成(*stu).Name

struct中所有字段的内存是连续的

Go 中的struct没有构造函数,一般通过工厂模式来解决,通过下面例子理解:

package main

import "fmt"

type Student struct {
    Name string
    Age  int
}

func newStudent(name string, age int) *Student {
    return &Student{
        Name: name, //后面需加上逗号,不然会报错
        Age:  age,  //后面需要加上逗号,不然会报错
    }
}

func main() {
    stu := newStudent("tom", 22)
    fmt.Println(stu)
    fmt.Println(stu.Name)
}

struct中的tag

我们可以为struct中的每个字段,写上一个tag。这个tag可以通过反射的机制获取到,最常用的场景就是json序列化和反序列化

下面先写一个正常我们序列化的例子:

package main

import (
    "encoding/json"
    "fmt"
)

type Student struct {
    Name string
    Age  int
}

func main() {
    var stu Student
    stu.Name = "tom"
    stu.Age = 22

    data, err := json.Marshal(stu)
    if err != nil {
        fmt.Printf("json marshal fail,fail message is %v\n", err)
        return
    }
    fmt.Printf("json marshal result is %s\n", data)
}

运行结果如下:

json marshal result is {"Name":"tom","Age":22}

注意:这里有个问题是我们在定义struct中的字段的时候如:Name,Age都是首字母大写的,这样你json序列化的时候才能访问到,如果是小写的,json包则无法访问到,所以就像上述的结果一样,序列化的结果也是首字母大写的,但是我就是想要小写怎么办?这里就用到了tag,将上述的代码更改为如下,序列化的结果就是小写的了:

package main

import (
    "encoding/json"
    "fmt"
)

type Student struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    var stu Student
    stu.Name = "tom"
    stu.Age = 22

    data, err := json.Marshal(stu)
    if err != nil {
        fmt.Printf("json marshal fail is %v\n", err)
        return
    }
    fmt.Printf("json marshal result %s\n", data)

    //反序列化到struct
    var stu2 Student
    err = json.Unmarshal(data, &stu2)
    if err != nil {
        fmt.Printf("unmarshal fail is %v\n", err)
        return
    }
    fmt.Printf("unmarshal result is %+v\n", stu2)
}

可以看到将json反序列化成结构的方式是

json.Unmarsha

结构体的比较

如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,那样的话,两个结构体将可以使用==或!=运算符进行比较。相等比较运算符将比较两个机构体的每个成员
如下面例子:

package main

import "fmt"

type Pointer struct {
    x int
    y int
}

func main() {
    p1 := Pointer{1, 2}
    p2 := Pointer{2, 3}
    p3 := Pointer{1, 2}
    fmt.Printf("p1==p2:%t\n", p1 == p2)
    fmt.Printf("p1==p3:%t\n", p1 == p3)
}

匿名字段

结构体中字段可以没有名字
下面是一个简单的例子:

package main

import "fmt"

type Student struct {
    Name string
    Age  int
    int
}

func main() {
    var stu Student
    stu.Name = "tom"
    stu.Age = 22
    stu.int = 111
    fmt.Printf("%+v\n", stu)
}

可能上面的这里例子看了之后感觉貌似也没啥用,其实,匿名字段的用处可能更多就是另外一个功能(其他语言叫继承),例子如下:

package main

import "fmt"

type People struct {
    Name string
    Age  int
}
type Student struct {
    People
    Score int
}

func main() {
    var stu Student
    // stu.People.Name = "tom"//是下面的简写
    // stu.People.Age = 22
    stu.Name = "tom"
    stu.Age = 22
    stu.Score = 100
    fmt.Printf("%+v\n", stu)
}

注意:关于字段冲突的问题,我们在People中定义了一个Name字段,在Student中再次定义Name,这个时候,我们通过s.Name获取的就是Student定义的Name字段

如下:

package main

import "fmt"

type People struct {
    Name string
    Age  int
    Sex  string
}
type Student struct {
    //字段冲突,重新定义一个Sex覆盖就行
    Sex int //放在People的上边或下边都行
    People
}

func main() {
    var stu Student
    stu.Name = "tom"
    stu.Age = 22
    stu.Sex = 1
    fmt.Printf("%+v\n", stu)
}

方法

首先强调一下:go中任何自定义类型都可以有方法,不仅仅是struct
注意除了:指针和interface

通过下面简单例子理解:

package main

import (
    "fmt"
)

//这里是我们普通定义的一个函数add
func add(a,b int) int {
    return a+b
}

type Int int

//这里是对Int这个自定义类型定义了一个方法add
func (i Int) add(a,b int) int{
    return a+b
}
//如果想要把计算的结果赋值给i
func(j *Int) add2(a,b int){
    *j = Int(a+b)
    return
}

func main(){
    c := add(100,200)
    fmt.Println(c)
    var b Int
    res := b.add(10,100)
    fmt.Println(res)

    var sum Int
    sum.add2(20,20)
    fmt.Println(sum)

}

方法的定义:

func(receiver type)methodName(参数列表)(返回值列表){

}

下面是给一个结构体struct定义一个方法

package main

import (
    "fmt"
)

type Student struct{
    Name string
    Age int
}

func (stu *Student)Set(name string,age int){
    stu.Name = name
    stu.Age = age
}

func main(){
    var s Student
    s.Set("tome",23)
    fmt.Println(s)
}

注意:方法的访问控制也是通过大小写控制的

在上面这个例子中需要注意一个地方func (stu *Student)Set(name string,age int)这里使用的是(stu *Student)而不是(stu Student)这里其实是基于指针对象的方法

基于指针对象的方法

当调用一个函数时,会对其每个参数值进行拷贝,如果一个函数需要更新一个变量,或者函数的其中一个参数是在太大,我们希望能够避免进行这种默认的拷贝,这种情况下我们就需要用到指针了,所以在上一个代码例子中那样我们需要func (stu *Student)Set(name string,age int)来声明一个方法

这里有一个代码例子:

package main

import "fmt"

type Pointer struct {
    X float64
    Y float64
}

func (p *Pointer) ScaleBy(f float64) {
    p.X *= f
    p.Y *= f
}

func main() {
    //method 1
    p1 := &Pointer{2, 3}
    p1.ScaleBy(3)
    fmt.Printf("p1:%f\n", *p1)

    //method 2
    p2 := Pointer{2, 3}
    p2r2 := &p2
    p2r2.ScaleBy(3)
    fmt.Printf("p2:%f\n", p2)

    //method 3
    p3 := Pointer{2, 3}
    (&p3).ScaleBy(3)
    fmt.Printf("p3:%f\n", p3)

    //method 4
    //相对来说方法2和方法3有点笨拙
    //方法4,go语言这里会自己判断p是一个Point类型的变量,
    //并且其方法需要一个Point指针作为指针接收器,直接可以用下面简单的方法
    p4 := Pointer{2, 3}
    p4.ScaleBy(3)
    fmt.Printf("p4:%f\n", p4)
}

上面例子中最后一种方法,编译器会隐式的帮我们用&p的方法去调用ScaleBy这个方法
当然这种简写方法只适用于变量,包括struct里面的字段,如:p.X

转自https://www.cnblogs.com/zhaof/p/8244542.html

原文地址:https://www.cnblogs.com/justdoyou/p/10033675.html

时间: 2024-08-03 05:57:47

golang之结构体和方法的相关文章

golang(07)结构体介绍

golang支持面向对象的设计,一般支持面向对象的语言都会有class的设计,但是golang没有class关键字,只有struct结构体.通过结构体达到类的效果,这叫做大成若缺,其用不弊. struct简介 在使用struct之前,先介绍golang的一个特性,golang允许用户将类型A定义为另一种类型B,并为类型B添加方法. 1234 type Integer int func (a Integer) Less (b Integer) bool{ return a < b} 我们将int定

Golang 入门 : 结构体(struct)

Go 通过类型别名(alias types)和结构体的形式支持用户自定义类型,或者叫定制类型.试图表示一个现实世界中的实体. 结构体由一系列命名的元素组成,这些元素又被称为字段,每个字段都有一个名称和一个类型. 结构体的目的就是把数据聚集在一起,以便能够更加便捷地操作这些数据.结构体的概念在 C 语言里很常见,被称为 struct.Golang 中的结构体也是 struct.Go 语言中没有类的概念,因此在 Go 中结构体有着更为重要的地位.结构体是复合类型(composite types),当

关于物体 &#39;固有类别&#39; 与 &#39;实际使用类别&#39; 分离的情况,结构体定义方法

在面向接口.面向对象编程的过程中,会遇到实际物体类别与定义类别相分离的情况. 例如,我们有三种物体,他们的固有类别分别为: TYPEA,TYPEB,TYPEC.在我们实际使用过程中,我们会根据不同的情况将他们分成 2 组: Group1,Group2. 那么,我们在定义结构体和物理类别时,需要注意对 Group 进行定义.定义物体具体属性和结构体如下: #define TYPEA (0x01UL << 0) #define TYPEB (0x01UL << 1) #define T

go结构体与方法

go结构体相当于python中类的概念 结构体用来定义复杂的数据结构,存储很多相同的字段属性 1.结构体的定义以及简单实用 package main import ( "fmt" ) func main() { type Student struct { //定义结构体 name string age int } s1 := new(Student) // 定义指向结构体的指针 s1.name = "xiaomu" s1.age = 10 fmt.Printf(&q

golang中结构体的初始化方法(new方法)

自定义一个结构体 type Rect struct { x, y float64 width, height float64 } 初始化方法: rect1 := new(Rect) rect2 := &Rect{} rect3 := &Rect{0, 0, 100, 200} rect4 := &Rect{width:100, height:200} 注意这几个变量全部为指向Rect结构的指针(指针变量),因为使用了new()函数和&操作符.而如果使用方法 a := Rec

C语言结构体初始化方法

早上苏凯童鞋问我这个问题来着,写在这里. 我了解到的C中结构体初始化的方法大概有三种. 如这里我定义了一个结构体: typedef struct node { int x, y; }Node; 第一种: Node a = {1, 2}; 第二种: Node b = {x:1, y:2}; 第三种: Node c = { .x = 1, .y = 2 } 当然,上述的初始化方法不是必须每个成员都要初始化,可以指定初始化. 附上一个不错的学习链接:点我

go_结构体和方法

面向对象:go语言仅支持封装不支持继承和多态 所以go语言没有class,只有struct(结构体) 原文地址:https://www.cnblogs.com/luffe/p/8548517.html

typedef struct 指针结构体使用方法

A>>>>>>>>>>>>>>>>>>>>>>>> typedef struct                {                    ...                    ...                }POINT,*POINT_P;  POINT为结构名,这个名字主要是为了在结构体中包含自己为成员变量的时候有用        

C++ STL——优先队列的结构体表示方法

优先队列是队列的一种,但是自身具有一定的排序功能,所以不具有队列“先进先出”的性质 刚刚接触优先队列,看过网上的用法后感觉还是太过于朦胧,所以打算自己写一个稍微细节一点的. 头文件 #include<queue> 常用操作 q.push() //放入元素 q.pop() //弹出元素 q.empty()//判断队列是否为空 q.top()//返回头部元素 q.size()//返回队列元素个数 声明方式 priority<int>q; 默认的情况是大顶锥,及先输出的是元素较大的: 如