一、struct简介

go语言中没有像类的概念,但是可以通过结构体struct实现oop(面向对象编程)。struct的成员(也叫属性或字段)可以是任何类型,如普通类型、复合类型、函数、map、interface、struct等,所以我们可以理解为go语言中的“类”。

二、struct详解

struct定义

在定义struct成员时候区分大小写,若首字母大写则该成员为公有成员(对外可见),否则是私有成员(对外不可见)。

type struct_variable_type struct {
member member_type
member member_type
.....
member member_type }
//示例
type Student struct {
name string
age int
Class string
}

声明与初始化

var stu1 Student
var stu2 *Student= &Student{} //简写stu2 := &Student{}
var stu3 *Student = new(Student) //简写stu3 := new(Student)

struct使用

在struct中,无论使用的是指针的方式声明还是普通方式,访问其成员都使用".",在访问的时候编译器会自动把 stu2.name 转为 (*stu2).name。

struct分配内存使用new,返回的是指针。

struct没有构造函数,但是我们可以自己定义“构造函数”。

struct是我们自己定义的类型,不能和其他类型进行强制转换。

package main

import "fmt"

type Student struct {
name string
age int
Class string
}
func main() {
var stu1 Student
stu1.age =
stu1.name = "wd"
stu1.Class = "class1"
fmt.Println(stu1.name) //wd var stu2 *Student = new(Student)
stu2.name = "jack"
stu2.age =
fmt.Println(stu2.name,(*stu2).name)//jack jack var stu3 *Student = &Student{ name:"rose",age:,Class:"class3"}
fmt.Println(stu3.name,(*stu3).name) //rose rose }

自定义构造函数

以下是通过工厂模式自定义构造函数方法

package main

import "fmt"

type Student struct {
name string
age int
Class string
} func Newstu(name1 string,age1 int,class1 string) *Student {
return &Student{name:name1,age:age1,Class:class1}
}
func main() {
stu1 := Newstu("wd",,"math")
fmt.Println(stu1.name) // wd
}

tag

tag可以为结构体的成员添加说明或者标签便于使用,这些说明可以通过反射获取到。

在前面提到了,结构体中的成员首字母小写对外不可见,但是我们把成员定义为首字母大写这样与外界进行数据交互会带来极大的不便,此时tag带来了解决方法。

type Student struct {
Name string "the name of student"
Age int "the age of student"
Class string "the class of student"
}

应用场景示例,json序列化操作:

package main

import (
"encoding/json"
"fmt"
) type Student struct {
Name string `json:"name"`
Age int `json:"age"`
} func main() {
var stu = Student{Name:"wd",Age:}
data,err := json.Marshal(stu)
if err != nil{
fmt.Println("json encode failed err:",err)
return
}
fmt.Println(string(data)) //{"name":"wd","age":22} }

匿名成员(字段、属性)

结构体中,每个成员不一定都有名称,也允许字段没有名字,即匿名成员。

匿名成员的一个重要作用,可以用来实现oop中的继承。

同一种类型匿名成员只允许最多存在一个。

当匿名成员是结构体时,且两个结构体中都存在相同字段时,优先选择最近的字段。

package main

import "fmt"

type Person struct {
Name string
Age int
}
type Student struct {
score string
Age int
Person
} func main() {
var stu = new(Student)
stu.Age = //优先选择Student中的Age
fmt.Println(stu.Person.Age,stu.Age)// 0,22
}

继承、多继承

当结构体中的成员也是结构体时,该结构体就继承了这个结构体,继承了其所有的方法与属性,当然有多个结构体成员也就是多继承。

访问父结构中属性也使用“.”,但是当子结构体中存在和父结构中的字段相同时候,只能使用:"子结构体.父结构体.字段"访问父结构体中的属性,如上面示例的stu.Person.Age

继承结构体可以使用别名,访问的时候通过别名访问,如下面示例man1.job.Salary:

package main

import "fmt"

type Person struct {
Name string
Age int
}
type Teacher struct {
Salary int
Classes string
} type man struct {
sex string
job Teacher //别名,继承Teacher
Person //继承Person } func main() {
var man1 = new(man)
man1.Age =
man1.Name = "wd"
man1.job.Salary =
fmt.Println(man1,man1.job.Salary) //&{ {8500 } {wd 22}} 8500
}

结构体中的方法

go语言中的方法是作用在特定类型的变量上,因此自定义的类型都可以有方法,不仅仅是在结构体中。

go中的方法和传统的类的方法不太一样,方法和类并非组织在一起,传统的oop方法和类放在一个文件里面,而go语言只要在同一个包里就可,可分散在不同文件里。go的理念就是数据和实现分离,引用官方说法:“Methods are not mixed with the data definition (the structs): they are orthogonal to types; representation(data) and behavior (methods) are independent”

方法的调用通过recv.methodName(),其访问控制也是通过大小写区分。

方法定义,其中recv代表方法作用的结构体:

func (recv type) methodName(parameter_list) (return_value_list) { … }
package main

import "fmt"

type Person struct {
Name string
Age int
} func (p Person) Getname() string{ //p代表结构体本身的实列,类似python中的self,这里p可以写为self
fmt.Println(p.Name)
return p.Name } func main() {
var person1 = new(Person)
person1.Age =
person1.Name = "wd"
person1.Getname()// wd
}

当有了结构的方法时候,我们可以自己定义其初始化方法,由于结构体是值类型,所以我们使用指针才能改变其存储的值。

package main

import "fmt"

type Person struct {
Name string
Age int
} func (self *Person) init(name string ,age int){
self.Name = name
self.Age = age
} func main() {
var person1 = new(Person)
person1.init("wd",)
//(&person1).init("wd",22)
fmt.Println(person1)//&{wd 22}
}

如果实现了结构体中的String方法,在使用fmt打印时候会调用该方法,类似与python中的__str__方法.

package main

import "fmt"

type Person struct {
Name string
Age int
} func (self *Person) String() string{
return self.Name } func main() {
var person1 = new(Person)
person1.Name = "wd"
person1.Age =
fmt.Println(person1)// wd
}

内存分布

go中的结构体内存布局和c结构体布局类似,每个成员的内存分布是连续的,在以下示例中通过反射进行进一步说明:

package main

import (
"fmt"
"reflect"
) type Student struct {
Name string
Age int64
wight int64
high int64
score int64 } func main() {
var stu1 = new(Student)
fmt.Printf("%p\n",&stu1.Name)
fmt.Printf("%p\n",&stu1.Age)
fmt.Printf("%p\n",&stu1.wight)
fmt.Printf("%p\n",&stu1.high)
fmt.Printf("%p\n",&stu1.score)
typ := reflect.TypeOf(Student{})
fmt.Printf("Struct is %d bytes long\n", typ.Size())
// We can run through the fields in the structure in order
n := typ.NumField()
for i := ; i < n; i++ {
field := typ.Field(i)
fmt.Printf("%s at offset %v, size=%d, align=%d\n",
field.Name, field.Offset, field.Type.Size(),
field.Type.Align())
}
} //结果
0xc42007a180
0xc42007a190
0xc42007a198
0xc42007a1a0
0xc42007a1a8
Struct is bytes long
Name at offset , size=, align=
Age at offset , size=, align=
wight at offset , size=, align=
high at offset , size=, align=
score at offset , size=, align=

在以上结果中,可以看到内存地址的偏移总是以8字节偏移(使用的是int64,刚好是8字节),在观察其内存地址,也是连续的,所以go语言中的结构体内存布局是连续的。如下图:

三、使用struct实现链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

链表有很多种不同的类型:单向链表,双向链表以及循环链表。

下面以单链表为例,使用go语言实现:

单链表

单链表:每个节点包含下一个节点的地址,这样把所有节点都串起来的链式数据数据结构叫做链表,通常把链表中的第一个节点叫做表头。

使用struct定义单链表:

为了方便,数据区域这里使用int

type Node struct {
data int
next *node
}

链表遍历

链表的遍历是通过移动指针进行遍历,当指针到最好一个节点时,其next指针为nil

package main

import "fmt"

type Node struct {
data int
next *Node
} func Shownode(p *Node){ //遍历
for p != nil{
fmt.Println(*p)
p=p.next //移动指针
}
} func main() {
var head = new(Node)
head.data =
var node1 = new(Node)
node1.data = head.next = node1
var node2 = new(Node)
node2.data = node1.next = node2
Shownode(head)
}
//{1 0xc42000e1e0}
//{2 0xc42000e1f0}
//{3 <nil>}

插入节点

单链表的节点插入方法一般使用头插法或者尾插法。

头插法:每次插入在链表的头部插入节点。

package main

import "fmt"

type Node struct {
data int
next *Node
} func Shownode(p *Node){ //遍历
for p != nil{
fmt.Println(*p)
p=p.next //移动指针
}
} func main() {
var head = new(Node)
head.data =
var tail *Node
tail = head //tail用于记录头节点的地址,刚开始tail的的指针指向头节点
for i := ;i<;i++{
var node = Node{data:i}
node.next = tail //将新插入的node的next指向头节点
tail = &node //重新赋值头节点
} Shownode(tail) //遍历结果
}
//{9 0xc42007a240}
//{8 0xc42007a230}
//{7 0xc42007a220}
//{6 0xc42007a210}
//{5 0xc42007a200}
//{4 0xc42007a1f0}
//{3 0xc42007a1e0}
//{2 0xc42007a1d0}
//{1 0xc42007a1c0}
//{0 <nil>}

尾插法:每次插入节点在尾部,这也是我们较为习惯的方法。

package main

import "fmt"

type Node struct {
data int
next *Node
} func Shownode(p *Node){ //遍历
for p != nil{
fmt.Println(*p)
p=p.next //移动指针
}
} func main() {
var head = new(Node)
head.data =
var tail *Node
tail = head //tail用于记录最末尾的节点的地址,刚开始tail的的指针指向头节点
for i := ;i<;i++{
var node = Node{data:i}
(*tail).next = &node
tail = &node
} Shownode(head) //遍历结果
} //{0 0xc42007a1c0}
//{1 0xc42007a1d0}
//{2 0xc42007a1e0}
//{3 0xc42007a1f0}
//{4 0xc42007a200}
//{5 0xc42007a210}
//{6 0xc42007a220}
//{7 0xc42007a230}
//{8 0xc42007a240}
//{9 <nil>}

go语言之行--结构体(struct)详解、链表的更多相关文章

  1. Go语言中的结构体 (struct)

    Golang官方称Go语言的语法相对Java语言而言要简洁很多,但是简洁背后也灵活了很多,所以很多看似很简单的代码上的细节稍不注意就会产生坑.本文主要对struct结构体的相关的语法进行总结和说明. ...

  2. 结构体指针,C语言结构体指针详解

    结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针. 指向结构体变量的指针 前面我们通过“结构体变量名.成员名”的方式引用结构体变量中的成员,除了这种方法之外还可以使用指针. 前面讲过,& ...

  3. inode结构体成员详解

    概述:inode译成中文就是索引节点,它用来存放档案及目录的基本信息,包含时间.档名.使用者及群组等.inode分为内存中的inode和文件系统中的inode,为了避免混淆,我们称前者为VFS ino ...

  4. Solidity的自定义结构体深入详解

    一.结构体定义 结构体,Solidity中的自定义类型.我们可以使用Solidity的关键字struct来进行自定义.结构体内可以包含字符串,整型等基本数据类型,以及数组,映射,结构体等复杂类型.数组 ...

  5. IPv4地址结构体sockaddr_in详解

    sockaddr_in结构体定义 struct sockaddr_in { sa_family_t sin_family; //地址族(Address Family) uint16_t sin_por ...

  6. struct stat结构体的详解和用法

    [cpp] view plaincopy //! 需要包含de头文件 #include <sys/types.h> #include <sys/stat.h> S_ISLNK( ...

  7. python之struct详解

    python之struct详解 2018-05-23 18:20:29 醉小义 阅读数 20115更多 分类专栏: python   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议 ...

  8. python struct详解

    转载:https://www.cnblogs.com/gala/archive/2011/09/22/2184801.html 有的时候需要用python处理二进制数据,比如,存取文件,socket操 ...

  9. Swift语言精要 - 浅谈结构体(Struct)

    CGRect, CGSize, CGPoint这些是 . String, Int, Array, Dictionary这些我们经常用的也是结构体(Struct). 那么结构体(Struct)到底是什么 ...

随机推荐

  1. SSM(Spring MVC +Spring+Mybatis)整合——maven工程

    所谓的SSM 其实就是Spring MVC下整合mybatis. 具体的定义网络上都有,很详细. 这里只说项目的搭建步骤. 第一步 新建maven工程 工程目录如下: 配置pom.xml文件,引入所需 ...

  2. 语义SLAM的数据关联和语义定位(二)Semantic Localization Via the Matrix Permanent

    论文假设和单目标模型 这部分想讲一下Semantic Localization Via the Matrix Permanent这篇文章的一些假设. 待求解的问题可以描述为 假设从姿态\(x\)看到的 ...

  3. 解决在IDEA 的Maven下 出现 Cannot access in offline mode 问题

    去掉maven前面的work offline模式

  4. Sql Server与.Net(C#)中星期值对比

    最近发现Sql Server与.Net(C#)中星期值居然不匹配,倒不知道依哪一个了. 1.Sql Server declare @date datetime; set @date = '2017-0 ...

  5. PFX文件提取公钥私钥

    jks是JAVA的keytools证书工具支持的证书私钥格式.pfx是微软支持的私钥格式. cer是证书的公钥. 如果是你私人要备份证书的话记得一定要备份成jks或者pfx格式,否则恢复不了. 简单来 ...

  6. 【Vue.js学习】生命周期及数据绑定

    一.生命后期 官网的图片说明: Vue的生命周期总结 var app = new Vue({ el:"#app", beforeCreate: function(){ consol ...

  7. MySQL优化—工欲善其事,必先利其器(2)

    上一篇文章简单介绍了下EXPLAIN的用法,今天主要介绍以下几点内容: 慢查询日志 打开慢查询日志 保存慢查询日志到表中 慢查询日志分析 Percona Toolkit介绍 安装 pt-query-d ...

  8. 自己模拟写C++中的String类型

    下面是模拟实现字符串的相关功能,它包括一下功能:    String(const char * s);//利用字符串来初始化对象    String(); //默认构造函数    String(con ...

  9. python基础之单例设计模式

    class Player(): instance = None init_flag = False def __init__(self): if self.init_flag is False: pr ...

  10. [python] 统计某一路径下所有代码真实行数(空行已被过滤)

      #-*- coding:utf-8 -*- ''' Created on 2018年8月15日 @author: anyd ''' import os list_line = [] filepat ...