引用曾经看到的一篇文章里面对 Golang 中结构体的描述,如果说 Golang 的基础类型是原子,那么

结构体就是分子。我们都知道分子是由原子组成的,换言之就是结构体里面可以包含基础类型、切片、

字典、数组以及结构体自身。

结构体类型的定义

结构体定义的一般方式

type identifier struct {
field1 type1
field2 type2
...
}

结构体里的字段都有名字,而对于字段在代码中从来也不被使用到,那么可以命名它为_。对于相同类型的

字段我们可以使用简写的形式,比如:

type T struct {
a,b int
}

还有一个需要注意的地方是结构体中字段的大小写,首字母大写表示公有变量,首字母小写表示私有变量,

相当于其他语言类成员变量的Public、Private ,但是这个公有变量和私有变量在 Go 中是相对于 Package 来

说的,在同一个 Package 都是公有的。

结构体变量的创建

1 最常见的创建结构体变量形式

package main

import "fmt"

type Circle struct {
x int
y int
Radius int
} func main() {
var c Circle = Circle{
x:100,
y:100,
Radius:50,
} fmt.Println(c)
}
--------------------------------
{100 100 50}

通过显示指定结构体内部字段的名称和初始值初始化结构体,可以只指定部分字段的初始值,或一个都不指定,

那些没有被指定初始值的字段会自动初始化为相应类型的零值。

2 结构体变量创建的第二种形式是不显示指定字段而是根据字段顺序初始化,但是这样需要显示的提供所有字段的

初始值

package main

import "fmt"

type Circle struct {
x int
y int
Radius int
} func main() {
var c Circle = Circle{100,100,50} fmt.Println(c)
}
-------------------------------
{100 100 50} 

3 使用 new() 函数创建结构体变量

结构体变量和普通变量都有指针形式,使用取地址符就可以得到结构体的指针类型

package main

import "fmt"

type Circle struct {
x int
y int
Radius int
} func main() {
var c *Circle = &Circle{100,100,50}
fmt.Printf("%+v\n", c)
}
------------------------------------------
&{x:100 y:100 Radius:50}

下面看一下使用 new() 创建一个零值结构体,所有的字段都会被初始化成对应类型的零值

package main

import "fmt"

type Circle struct {
x int
y int
Radius int
} func main() {
var c *Circle = new(Circle)
fmt.Printf("%+v\n", c)
}
-----------------------------------
&{x:0 y:0 Radius:0}

注意:new 函数返回的是指针类型

结构体的参数传递

看一个实例

package main

import "fmt"

// 声明一个结构体
type employee struct {
name,address string
height,weight float64
} // 定一个方法,该方法的参数是一个结构体,主要用于修改结构体成员中name的值
func modifyAttribute(emy employee) {
emy.name = "newer"
fmt.Println(emy)
} func main() {
// 初始化结构体
emy := employee{
name:"xiaoming",
address:"beijing",
height:172.0,
weight:75.3,
} // 打印修改前的值
fmt.Println(emy) // 调用modifyAttribute
modifyAttribute(emy) // 打印修改后值
fmt.Println(emy)
}
----------------------------------
输出结果
{xiaoming beijing 172 75.3}
{newer beijing 172 75.3}
{xiaoming beijing 172 75.3}

从上面的输出结果上来看,虽然在 modifyAttribute 方法中修改了 name 值,但是在 main 函数中打印 name 的

值并没有变化,说明这是一个值传递

我们把 modifyAttribute 函数的参数变成结构体的指针类型,如下

func modifyAttribute(emy *employee)  {
emy.name = "newer"
fmt.Println(emy)
} func main() {
// 初始化结构体
emy := employee{
name:"xiaoming",
address:"beijing",
height:172.0,
weight:75.3,
} // 打印修改前的值
fmt.Println(emy) // 调用modifyAttribute
modifyAttribute(&emy) // 打印修改后值
fmt.Println(emy)
}
----------------------------
输出结果
{xiaoming beijing 172 75.3}
&{newer beijing 172 75.3}
{newer beijing 172 75.3}

我们看到在函数  modifyAttribute 中的修改影响到了 main 函数中的 name 值,这里是引用传递

再看一个例子:编写扩大圆半径的函数

package main

import "fmt"

// 定义一个结构体 Circle
type Circle struct {
x int
y int
Radius int
} // 通过值传递扩大圆半径
func expandByValue(c Circle) {
c.Radius *= 2
} // 通过引用传递扩大圆半径
func expandByPointer(c *Circle) {
c.Radius *= 2
}
func main() {
c := Circle{
Radius:50,
} expandByValue(c)
fmt.Println(c) expandByPointer(&c)
fmt.Println(c)
}
--------------------------------
输出结果
{0 0 50}
{0 0 100}

 我们可以从上面的输出中再次看到通过值传递,在函数里面修改结构体的状态不会影响原有结构

体的状态,通过值传递就不一样。

结构体方法

    Go 语言不是面向对象的语言,在 Go 语言中没有类的概念,结构体正是类的替代品。类可以附加很多成员方法,

结构体也可以。看一个实例,如何给结构体绑定方法:

package main

import (
"fmt"
"math"
) // 定义一个结构体 Circle
type Circle struct {
x int
y int
Radius int
} // 计算圆的面积 第一个括号里面表示的是方法的接收者 这里方法的接收者是结构体 Circle
// Area() 表示函数名 float64 表示函数的返回值类型
func (c Circle) Area() float64{
return math.Pi * float64(c.Radius) * float64(c.Radius)
} // 计算圆的周长
func (c Circle) Circumference() float64 {
return 2 *math.Pi * float64(c.Radius)
} func main() {
// 初始化结构体
c := Circle{
Radius:50,
} fmt.Println(c.Area(),c.Circumference())
}
-------------------------------------------------
输出结果
7853.981633974483 314.1592653589793

结构体的指针方法 

   如果使用结构体方法的形式给 Circle 增加一个扩大圆半径的方法,会发现半径还是扩大不了

func (c Circle) expand()  {
c.Radius *= 2
}

 这个方法和前面的 expandByValue 函数是等价的,只是调整了一下第一个参数的位置,在参数传递的时候依然是值传递

,所以,还是无法起到扩大圆半径的作用,这个时候就需要使用结构体的指针方法

func (c *Circle) expand()  {
c.Radius *= 2
}

  结构体指针方法和值方法在调用上没有区别,只是一个可以改变结构体内部状态,另一个不会。另外指针方法可以使用结

构体变量调用,值方法也可以使用结构体指针变量使用

结构体变量调用指针方法(比如调用计算圆周长的方法):

package main

import (
"fmt"
"math"
) // 定义一个结构体 Circle
type Circle struct {
x int
y int
Radius int
} // 计算圆的周长
func (c *Circle) Circumference() float64 {
c.Radius *= 2
return 2 *math.Pi * float64(c.Radius)
} func main() {
// 初始化结构体
c := Circle{
Radius:50,
}
fmt.Println(c.Circumference())
}
-----------------------------------------
输出结果
628.3185307179587

使用结构体指针变量调用值方法:

package main

import (
"fmt"
"math"
) // 定义一个结构体 Circle
type Circle struct {
x int
y int
Radius int
} // 计算圆的面积
func (c Circle) Area() float64{
return math.Pi * float64(c.Radius) * float64(c.Radius)
} func main() {
// 初始化结构体
c := &Circle{
Radius:50,
} fmt.Println(c.Area())
}
---------------------------------
输出结果
7853.981633974483

内嵌结构体

   结构体作为一种变量可以嵌套在另一个结构体中作为一个字段使用,这种内嵌结构体在 Go 语言中称之为

组合

package main

import "fmt"

// 定义一个结构体
type Pointer struct {
x int
y int
} func (p Pointer) show() {
fmt.Println(p.x,p.y)
} // 定义另一个结构体
type Circle struct {
// 将结构体 Pointer 嵌套在 Circle
loc Pointer
Radius int
} func main() {
c := Circle{
loc:Pointer{
x:100,
y:100,
},
Radius:50,
} fmt.Println(c)
fmt.Println(c.loc)
fmt.Println(c.loc.x,c.loc.y)
c.loc.show()
}
-----------------------------------
输出结果
{{100 100} 50}
{100 100}
100 100
100 100

匿名内嵌结构体

还有一种特殊的内嵌结构体形式,内嵌的结构体不提供名称。这时外面的结构体直接继承内嵌结构体的所有

内部字段和方法

package main

import "fmt"

// 定义一个结构体
type Pointer struct {
x int
y int
} func (p Pointer) show() {
fmt.Println(p.x,p.y)
} // 定义另一个结构体
type Circle struct {
// 匿名内嵌结构体
Pointer
Radius int
} func main() {
c := Circle{
Pointer:Pointer{
x:100,
y:100,
},
Radius:50,
} fmt.Println(c)
fmt.Println(c.Pointer)
fmt.Println(c.x,c.y) //继承字段
fmt.Println(c.Pointer.x,c.Pointer.y)
c.show() // 继承方法
c.Pointer.show()
}
--------------------------------------
输出结果
{{100 100} 50}
{100 100}
100 100
100 100
100 100
100 100 

 Go 语言的结构体没有多态性

    我们知道面向对象语言的三大特性,封装、继承、多态。其中多态是指父类定义的方法可以调用子类实现的方法,不同的子类有不同的实现方法,

从而给父类的方法带来了多样的不同行为。而 Go 不是面向对象语言,它的结构体也不支持结构体,如下示例

package main

import "fmt"

// 定义一个fruit结构体
type Fruit struct { } func (f Fruit) eat() {
fmt.Println("eat Fruit")
} func (f Fruit) enjoy() {
fmt.Println("smell first")
f.eat()
fmt.Println("clean finally")
} // 定一个apple结构体
type Apple struct {
Fruit
} func (a Apple) eat() {
fmt.Println("eat apple")
} // 定义一个banana结构体
type Banana struct {
Fruit
} func (b Banana) eat() {
fmt.Println("eat banana")
} func main() {
apple := Apple{}
banana := Banana{} apple.enjoy()
banana.enjoy()
}
-----------------------------
输出结果
smell first
eat Fruit
clean finally
smell first
eat Fruit
clean finally

从上面的输出结果中可以看到,虽然外部结构体可以继承内部结构体的方法和字段,但是外部结构体的方法不能覆盖内部结构体的方法,

enjoy 方法调用的 eat 方法还是 Fruit 自己的 eat 方法,它并没有被外面结构体的方法覆盖掉

Go 结构体(Struct)的更多相关文章

  1. C语言结构体-struct

    知识点: 1)结构体的定义. 2)结构体的sizeof. 3)  结构体的指针. 1) 结构体的定义: 在逻辑上有一定关联的多个数据类型做为一整体进行操作的数据结构,它的关键字是struct.下面我将 ...

  2. 内核中用于数据接收的结构体struct msghdr(转)

    内核中用于数据接收的结构体struct msghdr(转) 我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构.在我们的虚拟机上发送icmp回显请求包,pin ...

  3. Swift中元组(Tuples),结构体(Struct),枚举(Enums)之间的区别

    Swift有许多种存储数据方式,你可以用枚举(enums),元组(tuples),结构体(structs),类(classes),在这篇文章中我们将比较枚举.元组.结构体之间区别,首先从最简单的开始- ...

  4. C语言 - 结构体(struct)比特字段(:) 详细解释

    结构体(struct)比特字段(:) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26722511 结构体(struc ...

  5. Go语言学习笔记(四)结构体struct & 接口Interface & 反射

    加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套: go中的struc ...

  6. 结构体(struct)大小

    结构体(struct)大小 本文参考链接:C语言结构体(struct)常见使用方法,链接中的实例代码经实践有几处不准确,本文在引用时已做更改 注意:在结构体定义时不能申请空间(除非是结构体变量),不可 ...

  7. 结构体struct sockaddr_in, struct sockaddr,struct in_addr

    一.结构体 struct sockaddr_in,  struct sockaddr,  struct in_addr struct sockaddr_in,  struct sockaddr,str ...

  8. GO开发[五]:golang结构体struct

    Go结构体struct Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性 ...

  9. 结构体struct、联合体union、枚举类型enum

    1.c语言中的类型 1)内置类型——char,short,int,float,double: 2)用户自定义类型(UDT)——struct结构体,union联合体,enum枚举类型 2.内存对齐 2. ...

  10. C++/C#:类Class与结构体Struct的区别

    C++中: 默认的访问控制.继承访问权限不同:struct时public的,class时 private的: 其它基本一样. C#中: struct是值类型,class是引用类型的: struct S ...

随机推荐

  1. 【JavaScript 6连载】二、函数(工厂模式)

    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"& ...

  2. 文本tfidf

    文本分类tf:词的频率 idf:逆文档频率 代码实例: # tf idf from sklearn.feature_extraction.text import TfidfVectorizer imp ...

  3. 关于mapreducer 读取hbase数据 存入mysql的实现过程

    mapreducer编程模型是一种八股文的代码逻辑,就以用户行为分析求流存率的作为例子 1.map端来说:必须继承hadoop规定好的mapper类:在读取hbase数据时,已经有现成的接口 Tabl ...

  4. jQuery获取子元素个数的方法

    //获取id=div1下的子元素的个数 $('#id').children().length; //获取id=div1下的p元素个数 $('#id').children('p').length;

  5. Django中程序中图片资源的路径问题(static文件夹的放置)

    步骤1:在settings.py文件的最后加上以下内容: STATIC_URL = '/static/'STATIC_ROOT = os.path.join(os.path.dirname(__fil ...

  6. Git命令cherry-pick,选择把一部分代码提交到另一个分支

    由于对git的使用还不是很熟悉,只是会基本的添加.提交.分支相关简单操作,在开发新需求的时候,需要涉及两个项目,一部分在新项目的新分支上开发的,另一部分是在老项目的老分支上开发的(这里忘了创建新分支) ...

  7. python的类和实例化对象

    一切皆对象,类也是对象,类来自于元类type,如果一个类没有声明自己的元类,默认它就是元类. 即类是元类的实例,通过type(类)会显示type,而实例来自于类. 类有两个属性,数据属性和函数属性,下 ...

  8. java Condition条件变量的通俗易懂解释、基本使用及注意点

    最近在看pthread方面的书,看到条件变量一节的时候,回忆了下java中条件变量的使用方式. java中条件变量都实现了java.util.concurrent.locks.Condition接口, ...

  9. VScode 好用插件集合(一)

    VScode 好用插件集合(一) 什么是VScode Visual Studio Code (简称 VS Code / VSC) 是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流的开发语言的语 ...

  10. topcoder srm 340 div1

    problem1 link $f[i][L][R]$表示计算到第$i$个,最小最大值是$L,R$时的最少个数. import java.util.*; import java.math.*; impo ...