GO基础之结构体

1 、什么是结构体 GO语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

2、什么是实例化?

  • Go结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正分配内存。因此必须在定义结构体并实例化后才能使用结构体实例化就是根据结构体定义的格式创建一份与格式一致的內存区域。
  • 结构体实例之间的内存是完全独立的。

package main

import "fmt"

func main() {
    //实例化方式一
    var stu1 Student
    fmt.Printf("stu1=%T,%v,%q \n", stu1, stu1, stu1)
    stu1.name = "zs"
    stu1.age = 12
    stu1.sex = 1
    fmt.Printf("stu1=%T,%v,%q \n", stu1, stu1, stu1)
    //实例化方式二
    stu2 := Student{}
    stu2.name = "David"
    stu2.age = 33
    stu2.sex = 1
    fmt.Printf("stu2=%T,%v,%q \n", stu2, stu2, stu2)
    //实例化方式三
    stu3 := Student{
        name: "Josh",
        age:  28,
        sex:  1,
    }
    fmt.Printf("stu3=%T,%v,%q \n", stu3, stu3, stu3)
    //实例化四
    stu4 := Student{"Ruby", 30, 0}
    fmt.Printf("stu3=%T,%v,%q \n", stu4, stu4, stu4)
    //实例化五
    //使用内置函数new()对结构体进行实例化,结构体实例化后形成指针类型的结构体
    //·new内置函数会分配内存。第一个参数是类型,而不是值,返回的值是指向该类型新分配的零值的指针。该函数用于创建某个类型的指针。
    stu5 := new(Student)
    (*stu5).name = "Running"
    (*stu5).age = 31
    stu5.sex = 0                                     //语法糖的方式
    fmt.Printf("stu3=%T,%v,%q \n", stu5, stu5, stu5) //stu3=*main.Student,&{Running 31 0},&{"Running" ‘\x1f‘ ‘\x00‘}

}

//结构体的定义
type Student struct {
    name string
    age  int
    sex  int8
}

语法糖的概念( Syntactic sugar)

  • 语法糖,也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达( PeterJ.Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。
  • 通常来说使用语法糖能够增加程序的可读性,从而减少程序代码岀错的机会
  • 结构体和数组中都含有语法糖。

package main

import "fmt"

func main() {
    arr := [4]int{1, 2, 3, 4}
    arr2 := &arr
    fmt.Println((*arr2)[3])
    //数组中的语法糖
    fmt.Println(arr2[3])

    //切片
    arr3 := []int{10, 20, 30, 40}
    arr4 := &arr3
    fmt.Println((*arr4)[3])
    //切片中没有语法糖
    //fmt.Println(arr4[3])
}

3、结构体是值类型传递

二、深拷贝与浅拷贝

struct 默认是深拷贝

package main

import "fmt"

func main() {
    stu1 := Student{"zs", 18, 0}
    fmt.Printf("stu1:=%T,%p,%v \n", stu1, &stu1, stu1)
    //stu1:=main.Student,0xc0000044a0,{zs 18 0}
    stu2 := stu1
    stu2.name = "ls"
    fmt.Printf("stu2:=%T,%p,%v \n", stu2, &stu2, stu2)
    //stu2:=main.Student,0xc000004520,{ls 18 0}

    fmt.Printf("stu1:=%T,%p,%v \n", stu1, &stu1, stu1)
    //stu1:=main.Student,0xc0000044a0,{zs 18 0}
    //修改stu2对stu1没有任何影响

}

实现浅拷贝

1、直接赋值指针地址

package main

import "fmt"

func main() {
    stu1 := Student{"zs", 18, 0}
    fmt.Printf("stu1:=%T,%p,%v \n", stu1, &stu1, stu1)
    //stu1:=main.Student,0xc00005a440,{zs 18 0}
    stu2 := &stu1
    stu2.name = "ls"
    fmt.Printf("stu2:=%T,%p,%v \n", stu2, stu2, stu2)
    //stu2:=*main.Student,0xc00005a440,&{ls 18 0}

    fmt.Printf("stu1:=%T,%p,%v \n", stu1, &stu1, stu1)
    //stu1:=main.Student,0xc00005a440,{ls 18 0}
    //修改stu2对stu1有影响
}

2、实现结构体浅拷贝:通过new()函数来实例化对象

package main

import "fmt"

func main() {
    //3、实现结构体浅拷贝:通过new()函数来实例化对象
    d4 := new(Dog)
    d4.name = "多多"
    d4.color = "棕色"
    d4.age = 1
    d4.kind = "巴哥犬"
    d5 := d4
    fmt.Printf("d4: %T , %v , %p \n", d4, d4, d4)
    //d4: *main.Dog , &{多多 棕色 1 巴哥犬} , 0xc000040080
    fmt.Printf("d5: %T , %v , %p \n", d5, d5, d5)

    fmt.Println("--------------------------------------")

    d5.color = "金色"
    d5.kind = "金毛"
    fmt.Println("d5修改后:", d5)
    //d5修改后: &{多多 金色 1 金毛}
    fmt.Println("d4:", d4)
    //d4: &{多多 金色 1 金毛}
}

type Dog struct {
    name  string
    color string
    age   int8
    kind  string
}

三、struct做函数参数

结构体对象或指针作为函数的参数及函数返回值

  • 结构体对象作为函数参数与结构体指针作为函数参数的不同
  • 结构体对象作为函数返回值与结构体指针作为函数返回值的不同

四、匿名结构体和匿名字段

匿名结构体

  • •没有名字的结构体。无需通过type关键字定义就可以直接使用。
  • •在创建匿名结构体时,同时要创建对象。
  • •匿名结构体由结构体定义和键值对初始化两部分组成
    //匿名结构体
    addr := struct {
        province, city string
    }{"陕西省", "西安市"}
    fmt.Println(addr)

结构体的匿名字段
结构体中的字段没有名字,只包含一个没有字段名的类型。这些字段被称为匿名字段。

  • •如果字段没有名字,那么默认使用类型作为字段名。
  • •注意:同一个类型只能写一个。
  • •结构体嵌套中采用匿名结构体字段可以模拟继承关系。

package main

import "fmt"

type User struct {
    string
    byte
    int8
    float64
}

func main() {
    //    实例化结构体
    user:= User{"Steven" , ‘m‘ , 35 , 177.5}
    fmt.Println(user)
    //如果想依次输出姓名、年龄、身高、性别
    fmt.Printf("姓名:%s \n" , user.string)
    fmt.Printf("身高:%.2f \n" , user.float64)
    fmt.Printf("性别:%c \n" , user.byte)
    fmt.Printf("年龄:%d \n" , user.int8)
}

五、结构体的聚合

•将一个结构体作为另一个结构体的属性(字段),这种结构就是结构体嵌套。
•结构体嵌套可以模拟面向对象中的两种关系:

  • 〇聚合关系:一个类作为另_个类的属性
  • 〇继承关系:一个类作为另一个类的子类。子类和父类。

package main

import "fmt"

type Address struct {
    province, city string
}

type Person struct {
    name    string
    age     int
    address *Address
}

func main() {
    //模拟结构体对象之间的聚合关系
    p := Person{}
    p.name = "Steven"
    p.age = 35

    //赋值方式1:
    addr := Address{}
    addr.province = "北京市"
    addr.city = "海淀区"
    p.address = &addr

    fmt.Println(p)
    fmt.Println("姓名:", p.name)
    fmt.Println("年龄:", p.age)
    fmt.Println("省:", p.address.province)
    fmt.Println("市:", p.address.city)
    fmt.Println("-----------------------")

    //修改Person对象的数据,是否会影响Address对象的数据?
    p.address.city = "昌平区"
    fmt.Println("姓名:", p.name)
    fmt.Println("年龄:", p.age)
    fmt.Println("省:", p.address.province)
    fmt.Println("市:", p.address.city)
    fmt.Println("addr市:", addr.city) //?是否会受到影响?
    fmt.Println("-----------------------")

    //修改Address对象的数据,是否会影响Person对象的数据?
    addr.city = "大兴区"
    fmt.Println("姓名:", p.name)
    fmt.Println("年龄:", p.age)
    fmt.Println("省:", p.address.province)
    fmt.Println("市:", p.address.city)
    fmt.Println("addr市:", addr.city) //?是否会受到影响?
    fmt.Println("-----------------------")

    //赋值方式2
    p.address = &Address{
        province: "陕西省",
        city:     "西安市",
    }
    fmt.Println(p)
    fmt.Println("姓名:", p.name)
    fmt.Println("年龄:", p.age)
    fmt.Println("省:", p.address.province)
    fmt.Println("市:", p.address.city)
    fmt.Println("-----------------------")
}

六、结构体嵌套模拟继承关系

•继承是传统面向对象编程中三大特征之一。用于描述两个类之间的关系。一个类(子类、派生类)继承于另一个类(父类、超类)。
•子类可以有自己的属性和方法,也可以重写父类已有的方法。
•子类可以直接访问父类所有的属性和方法。
•提升字段:
  〇在结构体中属于匿名结构体的字段称为提升字段,因为它们可以被访问,就好像它们属于拥有匿名结构字段的结构一样。
  〇换句话说,父类中的字段就是提升字段。
•继承的意义:避免重复代码、扩展类的功能
•采用匿名字段的形式就是模拟继承关系。而模拟聚合关系时一定要采用有名字的结构体作为字段

package main

import (
    "fmt"
)

type Person struct {
    name string
    age  int
    sex  string
}

type Student struct {
    Person
    schoolName string
}

func main() {
    //1、实例化并初始化Person
    p1 := Person{"Steven", 35, "男"}
    fmt.Println(p1)
    fmt.Println("-------------------")

    //2、实例化并初始化Student
    //    写法1:
    s1 := Student{p1, "北航软件学院"}
    printInfo(s1)

    //写法2:
    s2 := Student{Person{"Josh", 30, "男"}, "北外高翻学院"}
    printInfo(s2)

    //写法3:
    s3 := Student{Person: Person{
        name: "Penn",
        age:  19,
        sex:  "男",
    },
        schoolName: "北大元培学院",
    }
    printInfo(s3)

    //    写法4:
    s4 := Student{}
    s4.name = "Daniel"
    s4.sex = "男"
    s4.age = 12
    s4.schoolName = "北京十一龙樾"
    printInfo(s4)

}

func printInfo(s1 Student) {
    fmt.Println(s1)
    fmt.Printf("%+v \n", s1)
    fmt.Printf("姓名:%s, 年龄:%d , 性别:%s ,学校:%s \n", s1.name, s1.age, s1.sex, s1.schoolName)
    fmt.Println("-------------------")
}

七、结构体中的方法

• Go语言同时有函数和方法,方法的本质是函数,但是方法和函数又具有不同点。
1、 含义不同
•函数function是一段具有独立功能的代码,可以被反复多次调用,从而实现代码复用。而方法method是一个类的行为功能,只有该类的对象才能调用。
2、 方法有接受者,而函数无接受者
• Go语言的方法methods—种作用于特定类型变量的函数。这种特定类型变量叫做Receiver (接受者、接收者、接收器)。
•接受者的概念类似于传统面向对象语言中的this或self关键字。
• Go语言的接受者强调了方法具有作用对象,而函数没有作用对象。
•—个方法就是一个包含了接受者的函数。
• Go语言中,接受者的类型可以是任何类型,不仅仅是结构体,也可以是struct类型外的其他任何类型。
3、 函数不可以重名,而方法可以重名
•只要接受者不同,则方法名可以一样。

package main

import "fmt"
import "base"
type Employee struct {
    name , currency string
    salary float64
}
func main() {
    emp1 := Employee{"Daniel" , "$" , 2000}
    emp1.printSalary()//员工姓名:Daniel ,薪资:$2000.00
    emp1.updateSalary(1000)
    emp1.printSalary()//员工姓名:Daniel ,薪资:$1000.00
}

//printSalary方法
func (e Employee) printSalary() {
    fmt.Printf("员工姓名:%s ,薪资:%s%.2f \n", e.name , e.currency , e.salary)
}
func (e *Employee) updateSalary(num float64)  {
    e.salary=num
}

方法的继承与重写

package main

import "fmt"
import "base"

type Human struct {
    name, phone string
    age         int
}

type Student struct {
    Human
    school string
}

type Employee struct {
    Human
    company string
}

func main() {
    s1 := Student{Human{"Daniel", "15012345678", 13}, "十一龙樾"}
    e1 := Employee{Human{"Steven", "17812345678", 35}, "1000phone"}
    s1.printName()
    e1.printName()
    s1.SayHi()
    e1.SayHi()
}
//======================方法的继承=============================
func (h *Human) printName() {
    fmt.Println("大家好!我是%s ", h.name)
}

//=====================方法的重写=============================
func (h *Human) SayHi() {
    fmt.Printf("大家好!我是%s , 我%d岁 , 联系方式:%s \n", h.name, h.age, h.phone)
}

func (s *Student) SayHi() {
    fmt.Printf("大家好!我是%s , 我%d岁 , 我在%s上学,联系方式:%s \n", s.name, s.age, s.school, s.phone)
}

func (e *Employee) SayHi() {
    fmt.Printf("大家好!我是%s , 我%d岁 , 我在%s工作,联系方式:%s \n", e.name, e.age, e.company, e.phone)
}

原文地址:https://www.cnblogs.com/jalja/p/11795509.html

时间: 2024-08-01 13:09:47

GO基础之结构体的相关文章

[C/C++基础] 3.结构体、共用体、枚举

概述: 结构体和数组主要有两点不同,首先结构体可以在一个结构中声明不同的数据类型,其次相同结构的结构体变脸是可以相互赋值的. 共用体(联合体)和结构体都是由多个不同的数据类型成员组成,但在任何同一时刻,共用体值存放了一个被选中的成员.而结构体的所有成员都存在. C++的枚举(enum)工具提供了另外一种可以替代const来创建符号常量的方式,枚举表是枚举常量的集合. 3.1 结构体struct 结构体类型变量的定义一般形式为: struct 结构体类型名{ 类型1 成员名1; 类型2 成员名2;

黑马程序员——c语言基础:结构体和枚举---typedef的使用

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.什么是结构体 当一个整体由多个数据构成时,我们可以用数组来表示这个整体,但是数组有个特点:内部的每一个元素都必须是相同类型的数据. 在实际应用中,我们通常需要由不同类型的数据来构成一个整体,比如学生这个整体可以由姓名.年龄.身高等数据构成,这些数 据都具有不同的类型,姓名可以是字符串类型,年龄可以是整型,身高可以是浮点型. 为此,C语言专门提供了一种构造类型来解决上述问题,这就是结构体,它

Golang基础之结构体

Go语言中没有"类"的概念,也不支持"类"的继承等面向对象的概念.Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性. 类型别名和自定义类型 自定义类型 在Go语言中有一些基本的数据类型,如string.整型.浮点型.布尔等数据类型, Go语言中可以使用type关键字来定义自定义类型. 自定义类型是定义了一个全新的类型.我们可以基于内置的基本类型定义,也可以通过struct定义.例如: // 将MyInt定义为int类型 type MyInt i

Go语言基础之结构体

Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念.Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性. 类型别名和自定义类型 自定义类型 在Go语言中有一些基本的数据类型,如string.整型.浮点型.布尔等数据类型, Go语言中可以使用type关键字来定义自定义类型. 自定义类型是定义了一个全新的类型.我们可以基于内置的基本类型定义,也可以通过struct定义.例如: //将MyInt定义为int类型 type MyInt int 通过Type关键字的定义,

Swift基础(类,结构体,函数)

import Foundation // 创建一个类 class Student { // 属性(类的属性必须赋初值,如果不赋值,需要写自定义方法) var studentName: String var studentAge: Int // 方法 func fallInLove(girlName:String) { print("\(self.studentName)看上隔壁的\(girlName)") } // 构造方法(自定义init方法) init (studentName1:

【Go】语法基础之结构体

结构体的定义很简单: type Vertex struct { X, Y float64 } 可以理解为多个变量的集合. 结构体的使用: 1.直接使用: v := Vertex{1, 2} 或 var v = Vertex{1, 2} 2.通过new关键字: v := new(Vertex) 3.通过指针访问: v := &Vertex{1, 2} 为结构体添加方法: package main import ( "fmt" "math" ) type Ver

黑马程序员— C语言基础之结构体、枚举、预处理、typedef的使用

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 本章我们主要来学习之前所学的复杂数据类型中的同样也很重要的结构体和枚举的知识,以及C语言中的预处理,typedef的使用,简单介绍一下static和extern等一些比较杂的知识.在开始本章的新知识学习之前首先我们来学习一下根据变量的作用域的不同C语言一般把变量分为两种,局部变量和全局变量.下面我们来分别比较和体会一下这两种不同的变量类型: 根据变量的作用域,可以分为: 1. 局部变量: a.

C语言基础_结构体

一.结构体 1)声明 struct 用来声明结构体 作用:管理多个数据信息 struct student{ int num; //成员变量之间用;隔开 int age; char name[30]; float score; }Student;//分号不要忘记 2) 初始化 1.设置初始值使用{} 2.按照成员变量的顺序赋值 3.可以不设置信息,使用{0} Student stu1={1,18,"luofeng",90.0}; Student stu2={2,81,"sunj

C++基础之结构体(原来就是个幼儿园的书包-中学生的书包-小学生的书包-大学生的旅行包或者行李箱)

结构体我说的东西不多,但单独提成一篇文章来讲述的原因是因为有种小悲小喜,不像意外的会了二级指针那种中悲中喜,不像会了多线程那种大悲大喜 如果让你给别人讲一下变量是个什么东西,举个例子,你会举例int还是char,之前的我会int,因为int熟悉啊,熟悉后感觉很简单,拿捏的准一样 结构体很早就知道有这个东西了,很早也会创建,但一直在实践中避免碰到,这几天封装多线程可是用到了很多次,熟悉了也就不怕了 什么是结构体--------------------------------变量的打包(变量可以是同

【基础】结构体重载,用 char*作为std::map中的key

结构体重载 C++中,结构体是无法进行==,>,<,>=,<=,!=这些操作的,这也带来了很多不方便的地方,尤其是在使用STL容器的时候,如果我们可以往语句中传入结构体,一些事情将会变得很简单. bool operator 运算符 (const 结构体名称 b) const { return(什么时候这个运算符对结构体成立);//注意对此运算符使用this->元素名: } 用 char*作为std::map中的key 首先为什么要用 char*作为std::map中的key