什么是结构体

结构体(struct)是用户自定义的类型,它代表若干字段的集合,可以用于描述一个实体对象,类似java中的class,是golang面向对象编程的基础类型。

如何定义一个结构体

type Coordinate struct {
X, Y float32
}

语法:type <Name> struct{}
上述代码定义个一个名为Coordinate的结构体,里面包括了两个float32的变量X,Y,该结构体可用于表示一个平面坐标。

添加对象方法

在go语言中,对象方法在结构体定义的外部添加

type Coordinate struct {
X, Y float32
}
//打印坐标
func (coo *Coordinate) GetCoordinate() {
fmt.Printf("(%.2f,%.2f)\n", coo.X, coo.Y)
return
}

其中,func关键字后面的(coo *Coordinate),表示该函数传入一个指向Coordinate的指针,可通过指针变量coo来操作结构体的值 

几种结构体初始化

一、按原始字段顺序通过创建结构体

package main

import (
"fmt"
) type Coordinate struct {
X, Y float32
} //打印坐标
func (coo Coordinate) GetCoordinate() {
fmt.Printf("(%.6f,%.4f)\n", coo.X, coo.Y)
return
} func main() { p0 := Coordinate{1, 2}
p0.GetCoordinate() }

  

输出:(1.00,2.00),其中X=1,Y=2

二、按照自定义字段顺序进行初始化

package main

import (
"fmt"
) type Coordinate struct {
X, Y float32
} func (coo *Coordinate) GetCoordinate() {
fmt.Printf("X: %.5f Y: %.4f\n", coo.X, coo.Y)
}
func main() {
p0 := Coordinate{Y: 1, X: 2}
p0.GetCoordinate()
}

  

输出:X: 2.00000 Y: 1.0000

其中,func关键字后面的(coo *Coordinate),表示该函数传入一个指向Coordinate的指针,可通过指针变量coo来操作结构体的值。

三、通过new函数创建

package main

import (
"fmt"
) type Coordinate struct {
X, Y float32
} func (coo *Coordinate) GetCoordinate() {
fmt.Printf("X: %.5f Y: %.4f\n", coo.X, coo.Y)
}
func main() {
p0 := new(Coordinate)
p0.X = 2.012
p0.Y = 3.2645
p0.GetCoordinate()
}

  输出结果:X: 2.01200 Y: 3.2645

比较三种创建方式

其中,第一种与第二种,p0均为一个类型为Coordinate的实例,而第三种p0为一个指向Coordinate的指针,相当于var p0 *Coordinate = new(Coordinate)

一般在进行例如type T struct {a, b int}的结构体定义之后
习惯使用t := new(T)给该结构体变量分配内存,它返回指向已分配内存的指针。变量t是一个指向T的指针,此时结构体字段的值是它们所属类型的零值。
声明 var t T 也会给 t 分配内存,并零值化内存,但是这个时候 t是类型T。在这两种方式中,t 通常被称做类型T的一个实例(instance)或对象(Object)。var t *T = new(T)等价于t := new(T)。

 

package main

import (
"fmt"
) type Coordinate struct {
X, Y float32
} func (coo *Coordinate) GetCoordinate() {
fmt.Printf("X: %.5f Y: %.4f\n", coo.X, coo.Y)
}
func main() {
//p0是类型Coordinate的一个实例
p0 := Coordinate{1, 2} //给该结构体p2变量分配内存,它返回指向已分配内存的指针
p2 := new(Coordinate)
p2.X = 1
p2.Y = 2
p3 := &Coordinate{X: 1, Y: 2}
p4 := &Coordinate{1, 2} fmt.Println("-------输出p0-------")
fmt.Printf("%v\n%T\n", p0, p0)
fmt.Println("-------输出p2-------")
fmt.Printf("%v\n%T\n", p2, p2)
fmt.Println("-------输出p3-------")
fmt.Printf("%v\n%T\n", p3, p3)
fmt.Println("-------输出p4-------")
fmt.Printf("%v\n%T\n", p4, p4) }

  结果输出:

-------输出p0-------
{1 2}
main.Coordinate
-------输出p2-------
&{1 2}
*main.Coordinate
-------输出p3-------
&{1 2}
*main.Coordinate
-------输出p4-------
&{1 2}
*main.Coordinate

可以看出来,p2,p3,p4均为一个指针变量

添加值拷贝的对象方法

刚才说到了,添加一个对象方法,可以通过func (t *T) functionname()来创建,其中t为一个指针变量。我们也可以通过值拷贝的方式,添加一个对象方法,语法为func(t T) functionname()

package main

import (
"fmt"
) type Coordinate struct {
X, Y float32
} func (coo *Coordinate) GetCoordinate() {
fmt.Printf("X,Y is : %.4f %.5f\n", coo.X, coo.Y)
} //值拷贝对象方法
func (coo Coordinate) SetPosition01(a float32, b float32) {
coo.X = a
coo.Y = b
} //指针变量对象方法
func (coo *Coordinate) SetPosition02(a float32, b float32) {
coo.X = a
coo.Y = b
}
func main() {
p0 := Coordinate{1, 2}
fmt.Print("SetPosition02调用前:")
p0.GetCoordinate() p0.SetPosition02(0, 0)
fmt.Print("SetPosition02调用后:")
p0.GetCoordinate() p0.SetPosition01(-1.22, 1.44)
fmt.Print("SetPosition01调用后:")
p0.GetCoordinate()
}

  结果输出:

SetPosition02调用前:X,Y is : 1.0000 2.00000
SetPosition02调用后:X,Y is : 0.0000 0.00000
SetPosition01调用后:X,Y is : 0.0000 0.00000

 从程序输出中可以看出,调用SetPosition01方法,发生了值拷贝,即使在方法内改变了coo的值,外部的p0的值没有被改变。而SetPosition02方法中,coo为指向p0地址的指针,由于是通过指针变量修改了X,Y的值,所以调用完毕后,外部p0的值会被修改为(0,0)

匿名结构体

package main

import (
"fmt"
) func main() {
p_3d := struct {
X, Y, Z float32
}{1, 2, 3}
fmt.Println("-------输出p_3d-------")
fmt.Printf("%v\n%T\n", p_3d, p_3d) }

输出:

-------输出p_3d-------
{1 2 3}
struct { X float32; Y float32; Z float32 }

p_3d为一个包含X,Y,Z三个变量的匿名结构体

golang构造函数?

在Go语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以 NewXXX来命名,表示“构造函数”:
这一切非常自然,开发者也不需要分析在使用了new之后到底背后发生了多少事情。在Go语言中,一切要发生的事情都直接可以看到。
—— 《Go语言编程》

 

func NewRect(x, y, width, height float64) *Rect {
return &Rect{x, y, width, height}
}

 

变量、方法可见性

Go语言对关键字的增加非常吝啬,其中没有privateprotectedpublic这样的关键 字。要使某个符号对其他包(package)可见(即可以访问),需要将该符号定义为以大写字母开头,如:

 type Rect struct {
X, Y float64
Width, Height float64
}

这样,Rect类型的成员变量就全部被导出了,可以被所有其他引用了Rect所在包的代码访问到。 成员方法的可访问性遵循同样的规则,例如:

func (r *Rect) area() float64 {
return r.Width * r.Height
}

  

这样,Rectarea()方法只能在该类型所在的包内使用。
需要注意的一点是,Go语言中符号的可访问性是包一级的而不是类型一级的。在上面的例 子中,尽管area()Rect的内部方法,但同一个包中的其他类型也都可以访问到它。这样的可访问性控制很粗旷,很特别,但是非常实用。如果Go语言符号的可访问性是类型一级的,少不 了还要加上friend这样的关键字,以表示两个类是朋友关系,可以访问彼此的私有成员。

struct Tag

在处理json格式字符串的时候,经常会看到声明struct结构的时候,属性的右侧还有反引号括起来的内容。形如

type User struct {
UserId int `json:"user_id" bson:"user_id"`
UserName string `json:"user_name" bson:"user_name"`
}

struct成员变量标签(Tag)说明

要比较详细的了解这个,要先了解一下golang的基础,在golang中,命名都是推荐都是用驼峰方式,并且在首字母大小写有特殊的语法含义:包外无法引用。但是由经常需要和其它的系统进行数据交互,例如转成json格式,存储到mongodb啊等等。这个时候如果用属性名来作为键值可能不一定会符合项目要求。

反引号中的内容在golang中叫标签(Tag),在转换成其它数据格式的时候,会使用其中特定的字段作为键值。例如上例在转成json格式:

u := &User{UserId: 1, UserName: "tony"}

j, _ := json.Marshal(u)

fmt.Println(string(j))

// 输出内容:{"user_id":1,"user_name":"tony"}

  

 完整示例:

package main

import (
"encoding/json"
"fmt"
) type User struct {
UserId int `json:"user_id" bson:"user_id"`
UserName string `json:"user_name" bson:"user_name"`
} func main() { u := &User{UserId: 1, UserName: "tony"} j, _ := json.Marshal(u) k, _ := json.MarshalIndent(u, "", " ") fmt.Println(string(j))
fmt.Printf("%s\n%T\n", j, j)
fmt.Println(string(k))
fmt.Printf("%s\n%T\n", k, k)
}

  输出:

{"user_id":1,"user_name":"tony"}
{"user_id":1,"user_name":"tony"}
[]uint8
{
"user_id": 1,
"user_name": "tony"
}
{
"user_id": 1,
"user_name": "tony"
}
[]uint8

  

如果在属性中不增加标签说明

package main

import (
"encoding/json"
"fmt"
) type User struct {
UserId int
UserName string
} func main() { u := &User{UserId: 1, UserName: "tony"} j, _ := json.Marshal(u) k, _ := json.MarshalIndent(u, "", " ") fmt.Println(string(j))
fmt.Printf("%s\n%T\n", j, j)
fmt.Println(string(k))
fmt.Printf("%s\n%T\n", k, k)
}

  则输出:

{"UserId":1,"UserName":"tony"}
{"UserId":1,"UserName":"tony"}
[]uint8
{
"UserId": 1,
"UserName": "tony"
}
{
"UserId": 1,
"UserName": "tony"
}
[]uint8

 

可以看到直接用struct的属性名做键值。

其中还有一个bson的声明,这个是用在将数据存储到mongodb使用的。

struct成员变量标签(Tag)获取

那么当我们需要自己封装一些操作,需要用到Tag中的内容时,如何去获取呢?这边可以使用反射包(reflect)中的方法来获取:

t := reflect.TypeOf(u)
field := t.Elem().Field(0)
fmt.Println(field.Tag.Get("json"))
fmt.Println(field.Tag.Get("bson"))

 

完整示例:

package main

import (
"encoding/json"
"fmt"
"reflect"
) func main() {
type User struct {
UserId int `json:"user_id" bson:"userid"`
UserName string `json:"user_name" bson:"username"`
}
// 输出json格式
u := &User{UserId: 1, UserName: "tony"}
j, _ := json.Marshal(u) //return ([]byte, error)
fmt.Printf("%c\n", j)
fmt.Println(string(j)) // 输出内容:{"user_id":1,"user_name":"tony"} // 获取tag中的内容
t := reflect.TypeOf(u)
field := t.Elem().Field(0)
fmt.Println(field.Tag.Get("json"))
// 输出:user_id
fmt.Println(field.Tag.Get("bson"))
// 输出:user_id }

  结果输出:

[{ " u s e r _ i d " : 1 , " u s e r _ n a m e " : " t o n y " }]
{"user_id":1,"user_name":"tony"}
user_id
userid

  

beego的ORM中也通过tag来定义参数的。

package main

import (
"fmt"
"reflect"
) type Job struct {
AlarmStatus *string `json:"alarm_status" name:"alarmstatus"`
CPUTopology string `json:"cpu_topology" name:"cputopology"`
} func main() {
a := "abc"
s := Job{&a, "hello"}
st := reflect.TypeOf(s)
field := st.Field(1)
fmt.Println(field.Tag.Get("json"), field.Tag.Get("name"))
}

  

package main

import (
"fmt"
"reflect" // 这里引入reflect模块
) type User struct {
Name string "user name" //这引号里面的就是tag
Passwd string "user passsword"
} func main() {
user := &User{"chronos", "pass"}
s := reflect.TypeOf(user).Elem() //通过反射获取type定义
for i := 0; i < s.NumField(); i++ {
fmt.Println(s.Field(i).Tag) //将tag输出出来
}
}

  

 

goang学习笔记---struct的更多相关文章

  1. C语言学习笔记--struct 和 union关键字

    1.struct关键字 C 语言中的 struct 可以看作变量的集合struct中的每个数据成员都有独立的存储空间. 结构体与柔性数组 (1)柔性数组即数组大小待定的数组 (2)C 语言中可以由结构 ...

  2. golang 学习笔记 -- struct interface的使用

    一个 interface 类型定义了一个方法集做接口. 区分goalng的方法和函数 func go() { fmt.Println('go to home') } 这是函数 type car str ...

  3. swift学习笔记5——其它部分(自动引用计数、错误处理、泛型...)

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

  4. swift学习笔记4——扩展、协议

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

  5. swift学习笔记3——类、结构体、枚举

    之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...

  6. Golang 语法学习笔记

    Golang 语法学习笔记 包.变量和函数. 包 每个 Go 程序都是由包组成的. 程序运行的入口是包 main. 包名与导入路径的最后一个目录一致."math/rand" 包由 ...

  7. linux驱动开发之块设备学习笔记

    我的博客主要用来存放我的学习笔记,如有侵权,请与我练习,我会立刻删除.学习参考:http://www.cnblogs.com/yuanfang/archive/2010/12/24/1916231.h ...

  8. BZOJ 2120 数颜色&2453 维护队列 [带修改的莫队算法]【学习笔记】

    2120: 数颜色 Time Limit: 6 Sec  Memory Limit: 259 MBSubmit: 3665  Solved: 1422[Submit][Status][Discuss] ...

  9. C++学习笔记(3)

    本学习笔记是C++ primer plus(第六版)学习笔记.是C++学习笔记(2)的后续.复习C++基础知识的可以瞄瞄. 转载请注明出处http://www.cnblogs.com/zrtqsk/p ...

随机推荐

  1. 英语Lignaloes沉香木LIGNALOES单词

    中文名沉香木 外文名Lignaloes 国内分布两广以及云南和福建等地 国外分布印度尼西亚.马来西亚.新加坡 沉香木是珍贵的香料,被用作燃烧熏香.提取香料.加入酒中,或直接雕刻成装饰品.沉香木又名沉水 ...

  2. 【APS系统应用案例】高博通信智能制造的新武器

    企业背景: 早在2006年,一个年轻的企业瞄准国际高端航空产业及超精密制造行业.高博集团,以普世价值开创航空中国的新纪元. 高博通信(上海)有限公司(下文简称“高博通信”)占地36000平方米,以满足 ...

  3. PMP备考-第一章-引论

    项目 项目是为创造独特的产品,服务和成果而进行的临时性工作.在规定的范围,进度,成本,和质量要求之下完成项目可交付成果. 项目与运用 项目 :临时性,独特性,渐进明细 运营 :持续的,相似性,标准化 ...

  4. [linux]在使用rsync时需要注意的小细节

    很简单:前一个目录末尾是目录的话,最后是否带/是有区别的. 具体看测试: # usr @ the-pc in ~/cptest02 [2:28:02] $ ll 总用量 0 # usr @ the-p ...

  5. BCD解密

    #include<stdio.h> int main(void) { int num; scanf_s("%d", &num); printf( * + num ...

  6. 4-剑指offer: 把数组排成最小的数

    题目描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323. 代码: cl ...

  7. loadrunner 添加检查点

    1.首先使用录制的方式,把论坛的代码编写完成后,要判断的执行前插入检查点,使用右键方式,弹出的下拉的菜单中进行选择“insert”的选项. 2.就在弹出下一级的菜单中,进行选中一个为“new step ...

  8. nginx发布静态资源

    nginx发布静态资源 参考 ngx_http_index_module index指令 ngx_http_core_module http指令 location指令 listen指令 root指令 ...

  9. SGD的动量(Momentum)算法

    引入动量(Momentum)方法一方面是为了解决“峡谷”和“鞍点”问题:一方面也可以用于SGD 加速,特别是针对高曲率.小幅但是方向一致的梯度. 如果把原始的 SGD 想象成一个纸团在重力作用向下滚动 ...

  10. LG4171/BZOJ1823 「JSOI2010」满汉全席 2-SAT

    问题描述 LG4171 BZOJ1823 题解 显然,每个评委对每个材料的满式/汉式要求是对\(n\)个元素的\(0,1\)取值限制. 显然想到\(\mathrm{2-SAT}\) 于是就可以切掉了. ...