golang之结构体和方法(代码片段)

justdoyou justdoyou     2023-01-26     642

关键词:

结构体的定义

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

其实简单理解,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
 age:%d
 sex:%s
 score:%d
", stu.Name, stu.Age, stu.Sex, stu.Score)

    fmt.Printf("%v
", stu)
    fmt.Printf("%+v
", stu)
    fmt.Printf("%#v
", 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
", err)
        return
    
    fmt.Printf("json marshal result is %s
", 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
", err)
        return
    
    fmt.Printf("json marshal result %s
", data)

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

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

json.Unmarsha

结构体的比较

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

package main

import "fmt"

type Pointer struct 
    x int
    y int


func main() 
    p1 := Pointer1, 2
    p2 := Pointer2, 3
    p3 := Pointer1, 2
    fmt.Printf("p1==p2:%t
", p1 == p2)
    fmt.Printf("p1==p3:%t
", 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
", 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
", 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
", 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 := &Pointer2, 3
    p1.ScaleBy(3)
    fmt.Printf("p1:%f
", *p1)

    //method 2
    p2 := Pointer2, 3
    p2r2 := &p2
    p2r2.ScaleBy(3)
    fmt.Printf("p2:%f
", p2)

    //method 3
    p3 := Pointer2, 3
    (&p3).ScaleBy(3)
    fmt.Printf("p3:%f
", p3)

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

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

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
























八结构体和接口(代码片段)

结构体定义:和C++ 一样,Golang的结构体也是封装数据。可以说是面向对象吧。结构体的组合函数:packagemainimport("fmt")typeNodestructx,yint//结构体外接函数(能不能在结构体内写,目前还不清楚能不能在内部定义func(nodeNode)area()(re... 查看详情

c和指针之结构体和联合体(代码片段)

1、结构体基础知识  聚合数据类型(aggregatedatatype)能够同时存储超过一个的单独数据。C语言提供了两种类型的聚合数据结构,数组和结构体。  数组是相同类型的数据元素的集合,它的每个元素都是通过下标引用或者指针间... 查看详情

golang中级进阶(二):结构体

...构体3.结构体标签tag4.嵌套结构体和json的序列化反序列化Golang中没有“类”的概念,Golang中的结构体和其他语言中的类有点相似。和其他面向对象语言中的类相比,Golang中的结构体具有更高的扩展性和灵活性。Golang中的基础数据... 查看详情

golang碎片整理之结构体(代码片段)

...集合,可以用于描述一个实体对象,类似Java中的class,是golang面向对象编程的基础。结构体的概念在软件工程上的旧术语是ADT(抽象数据类型:AbstractDatetype)。在c++它也是存在,并且名字也是struct,在面向对象的编程语言中,跟一... 查看详情

golang面向对象编程(代码片段)

阅读目录Golang面向对象编程11struct声明和定义struct初始化方法1struct初始化方法2结构体类型指针2struct的内存布局及构造函数结构体没有构造函数,必要时需要自己实现匿名字段和嵌套3匿名字段和struct嵌套匿名结构体匿名结构... 查看详情

golang使用带有结构的方法(代码片段)

查看详情

golang数据结构之冒泡排序(代码片段)

//BubbleSort冒泡排序funcBubbleSort(arr*[7]int)fori:=len(arr)-1;i>=0;i--forj:=i;j>=0;j--if(*arr)[j]>(*arr)[i](*arr)[j],(*arr)[i]=(*arr)[i],(*arr)[j]fmt.Printf("第%d趟的结果为:%v",len(arr)-1-i,*arr)   查看详情

golang之结构体(代码片段)

构造函数Go语言的结构体没有构造函数,我们可以自己实现。例如,下方的代码就实现了一个person的构造函数。因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型... 查看详情

golang数据结构之插入排序(代码片段)

//InsertSort插入排序funcInsertSort(arr*[7]int)fori:=1;i<len(arr);i++insertVal:=(*arr)[i]inserIndex:=i-1forinserIndex>=0&&(*arr)[inserIndex]>insertVal(*arr)[inserIndex+1]=(*arr)[inserIndex]inserIndex--//插入if(inserIndex+1)!=i(*arr)[inserIndex+1]=insertValfmt.Printf("第%d次... 查看详情

golang数据结构之选择排序(代码片段)

//SelectSort选择排序funcSelectSort(arr*[7]int)fori:=0;i<len(arr);i++tmp:=arr[i]index:=iforj:=i+1;j<len(arr);j++if(*arr)[j]<tmptmp=(*arr)[j]index=jifindex!=i(*arr)[index],(*arr)[i]=(*arr)[i],(*arr)[index]fmt.Printf("第%d次选择后的结果是:%v",i,*arr)   查看详情

golang数据结构和算法之circularbuffer环形缓冲队列(代码片段)

慢慢练语法和思路,想说的都在代码及注释里。CircularBufferpackageCircularBufferconstarraySize=10typeCircularBufferstruct data[arraySize]int pointerint//只实现了CircularBuffer环形缓冲队列的基本方法func(b*CircularBuffer)InsertValue(iint) ifb.pointer==len(b.data) b.po... 查看详情

golang之webassembly篇(代码片段)

解决Wasm和Js之间互调问题,程序猿直接上代码更清晰。DEMO项目结构分别贴出核心三个文件代码main.gopackagemainimport( "fmt" "learn/util" "syscall/js")funcmain() fmt. 查看详情

golang之webassembly篇(代码片段)

解决Wasm和Js之间互调问题,程序猿直接上代码更清晰。DEMO项目结构分别贴出核心三个文件代码main.gopackagemainimport( "fmt" "learn/util" "syscall/js")funcmain() fmt. 查看详情

[golang]语法基础之结构体(代码片段)

说明Go语言是一种静态类型的编程语言。正因为如此,编译器就需要在进行编译时知道程序当中每个值的类型。当知道了这些类型信息,编译器就可以合理的使用值。这样的一种形式能够减少潜在的内存异常和bug,同时使编译器... 查看详情

golang数据结构之快速排序(代码片段)

具体过程:黑色标记代表左指针,红色标记代表右指针,蓝色标记代表中间值。(依次从左往向下) //QuickSort快速排序funcQuickSort(leftint,rightint,arr*[7]int)l:=leftr:=rightpivot:=arr[(left+right)/2]tmp:=0forl<rforarr[l]<pivotl++forarr[r]>pivotr-... 查看详情

go的struct结构体和(jsonformtag)(代码片段)

Go的Struct结构体和(JsonFormtag)1.Struct1.1定义使用struct关键字可以定义一个结构体,结构体中的成员,称为结构体的字段或属性。typeMemberstructidintname,emailstringgender,ageinttypeMemberstructidintnamestringemailstringgenderintageinttype和s 查看详情

golang之继承,多重继承(struct)

热乎的代码来了packagemainimport"fmt"/*继承一个结构体嵌到另一个结构体,称作组合匿名和组合的区别如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现继承如果一个struct嵌套了另一个... 查看详情

golang注释规范(代码片段)

注释的意义注释可以帮我们很好的完成文档的工作,写得好的注释可以方便我们以后的维护。/**/的块注释和//的单行注释两种注释风格,在我们的项目中为了风格的统一,全部使用单行注释,注释的质量决定了生成的文档的质量... 查看详情