5 map、结构体、方法、面向对象【Go语言教程】

news2024/11/18 13:28:42

5 map、结构体、方法、面向对象【Go语言教程】

1 map

1.1 概念及声明

①概念

基本语法:var map 变量名 map[keytype]valuetype

  • key 可以是什么类型?
    golang 中的 map,的 key 可以是很多种类型,比如 bool, 数字,string, 指针, channel , 还可以是只包含前面几个类型的 接口, 结构体, 数组
    通常 key 为 int 、string
    注意: slice, map 还有 function 不可以,因为这几个没法用 == 来判断
  • valuetype 可以是什么类型?
    valuetype 的类型和 key 基本一样,这里我就不再赘述了通常为: 数字(整数,浮点数),string,map,struct

②声明方式

  1. var a map[string]string
  2. var a map[string]int
  3. var a map[int]string
  4. var a map[string]map[string]string
    注意:声明是不会分配内存的,初始化需要 make ,分配内存后才能赋值和使用
func main(){
	//map的声明和注意事项
	var a map[string]string
	//在使用map之前,需要先make(给map分配内存空间)
	a = make(map[string]string, 10)
	a["no1"] = "curry"
	a["no2"] = "徐杰"
	a["no3"] = "John"
	a["no4"] = "欧文"
	a["no3"] = "Bob"
	fmt.Println(a)
}

在这里插入图片描述

对上面的代码进行说明:

  1. map 在使用前一定要 make
  2. map 的 key 是不能重复,如果重复了,则以最后这个 key-value 为准
  3. map 的 value 是可以相同的.
  4. map 的 key-value 是无序
  5. make 内置函数数目

在这里插入图片描述
map的三种初始化方式:

package main
import (
	"fmt"
)

func main(){
	//map的初始化方式
	//1. 先声明后make
	var a map[string]string
	//在使用map前,需要先make(给map分配数据空间)
	a = make(map[string]string, 10)
	a["no1"] = "天暗星"
	a["no2"] = "天捷星"
	a["no3"] = "天速星"
	fmt.Println(a)
	//2. 声明时直接make
	cities := make(map[string]string)
	cities["no1"] = "newYork"
	cities["no2"] = "taibei"
	cities["no3"] = "beijing"
	fmt.Println(cities)
	//3. 声明时,直接初始化并赋值
	heros := map[string]string{
		"hero1": "宋江",
		"hero2": "赵云",
		"hero3": "岳飞",
	}
	heros["hero3"] = "林冲"
	fmt.Println("heros=", heros)
	// map[no1:天暗星 no2:天捷星 no3:天速星]
	// map[no1:newYork no2:taibei no3:beijing]     
	// heros= map[hero1:宋江 hero2:赵云 hero3:林冲]	
}

1.2 map的crud

①map的增加和更新

map[“key”] = value //如果 key 还没有,就是增加,如果 key 存在就是修改。

func main(){
	a := make(map[string]string)
	//map中没有存在key1,因此直接新增
	a["key1"] = "jack"
	//map中已经存在了key1,因此直接更新
	a["key1"] = "jackson"
	//map[key1:jackson]
	fmt.Println(a)
}

②map的删除

delete(map,“key”) ,delete 是一个内置函数,如果 key 存在,就删除该 key-value,如果 key 不存在, 不操作,但是也不会报错
在这里插入图片描述
在这里插入图片描述

func main(){
	a := make(map[string]string)
	a["key1"] = "jack"
	//删除key1
	delete(a, "key1")
	//map[]
	fmt.Println(a)
}
  • 注意:如果我们要删除 map 的所有 key ,没有一个专门的方法一次删除,可以遍历一下 key, 逐个删除或者 map = make(…),make 一个新的,让原来的成为垃圾,被 gc 回收
    在这里插入图片描述
    ③map的查找
    在这里插入图片描述

对上面代码的说明:
说明:如果 heroes 这个 map 中存在 “no1” , 那么 findRes 就会返回 true,否则返回 false

func main(){
	a := make(map[string]string)
	a["key1"] = "jack"
	a["key2"] = "tom"
	val, ok := a["key3"]
	if ok {
		fmt.Println("值为:", val)
	}else {
		fmt.Println("没找到")
	}
}

1.3 map的遍历与切片

①map的遍历

  • 案例演示相对复杂的 map 遍历:该 map 的 value 又是一个 map
    说明:map 的遍历使用 for-range 的结构遍历
package main
import (
	"fmt"
)

func main(){
	//map通过for-range方式来进行遍历
	//1. 遍历普通map
	cities := make(map[string]string)
	cities["no1"] = "北京"
	cities["no2"] = "天津"
	cities["no3"] = "上海"
	for k, v := range cities {
		fmt.Printf("k=%v v=%v\n", k, v)
	}
	//2. 遍历双重map【map的value又是一个map】
	studentMap := make(map[string]map[string]string)
	//map使用之前需要make make(map[string]string, 3)
	studentMap["stu01"] = make(map[string]string, 3)
	studentMap["stu01"]["name"] = "jack"
	studentMap["stu01"]["sex"] = "男"
	studentMap["stu01"]["address"] = "北京胡同"

	studentMap["stu02"] = make(map[string]string)
	studentMap["stu02"]["name"] = "lucy"
	studentMap["stu02"]["sex"] = "女"
	studentMap["stu02"]["address"] = "上海外滩"

	for k1, v1 := range studentMap {
		fmt.Println("k1=", k1)
		for k2, v2 := range v1 {
			fmt.Printf("\t k2=%v v2=%v\n", k2, v2)
		}
		fmt.Println()
	}
}

在这里插入图片描述
在这里插入图片描述
②map切片

切片的数据类型如果是 map,则我们称为 slice of map,map 切片,这样使用则 map 个数就可以动态变化了。

案例:

要求:使用一个 map 来记录 monster 的信息 name 和 age, 也就是说一个 monster 对应一个 map,并且妖怪的个数可以动态的增加=>map 切片

package main
import (
	"fmt"
)

func main(){
	//1. 声明一个map切片
	var monsters []map[string]string
	monsters = make([]map[string]string, 2) //准备放入2个妖怪
	monsters[0] = make(map[string]string, 2)
	monsters[0]["name"] = "牛魔王"
	monsters[0]["age"] = "500"

	monsters[1] = make(map[string]string, 2)
	monsters[1]["name"] = "狐狸精"
	monsters[1]["age"] = "300"
	//下面写法越界,因为只开辟了两块空间
	//panic: runtime error: index out of range [2] with length 2 
	// monsters[2] = make(map[string]string, 2)
	//这里我们可以使用切片的append函数,动态的增加monster
	//①定义monster信息
	newMonster := map[string]string{
		"name" : "新妖怪~火云邪神",
		"age" : "200",
	}
	monsters = append(monsters, newMonster)
	//[map[age:500 name:牛魔王] map[age:300 name:狐狸精] map[age:200 name:新妖怪~火云邪神]]
	fmt.Println(monsters)
}

1.3 map的排序及使用细节

①map的排序

  1. golang 中没有一个专门的方法针对 map 的 key 进行排序
  2. golang 中的 map 默认是无序的,注意也不是按照添加的顺序存放的,你每次遍历,得到的输出可能不一样.
  3. golang 中 map 的排序,是先将 key 进行排序,然后根据 key 值遍历输出即可
package main
import (
	"fmt"
	"sort"
)

func main(){
	//map的排序
	map1 := make(map[int]int, 10)
	map1[10] = 100
	map1[1] = 13
	map1[4] = 56
	map1[0] = 90

	fmt.Println(map1)

	//map的排序:
	//1. 先将map的key放入到切片中
	//2. 对切片进行排序
	//3. 遍历切片,然后按照key来输出map的值
	var keys []int
	for k, _ := range map1 {
		keys = append(keys, k)
	}
	//排序
	sort.Ints(keys)
	//根据排序后的切片输出map中key对应的value
	for _, k := range keys {
		fmt.Printf("map1[%v]=%v \n", k, map1[k])
	}
}

在这里插入图片描述
②使用细节

  1. map 是引用类型,遵守引用类型传递的机制,在一个函数接收 map,修改后,会直接修改原来的 map
    在这里插入图片描述
  1. map 的容量达到后,再想 map 增加元素,会自动扩容,并不会发生 panic,也就是说 map 能动态的增长 键值对(key-value)
  2. map 的 value 也经常使用 struct 类型,更适合管理复杂的数据(比前面 value 是一个 map 更好),比如 value 为 Student 结构体
    在这里插入图片描述

2 结构体(面向对象特性)

2.1 概念

Golang中面向对象的特点:

  1. Golang 也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说 Golang 支持面向对象编程特性是比较准确的。

  2. Golang 没有类(class),Go 语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解 Golang 是基于 struct 来实现 OOP 特性的。

  3. Golang 面向对象编程非常简洁,去掉了传统 OOP 语言的继承、方法重载、构造函数和析构函数、隐藏的 this 指针等等

  4. Golang 仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它 OOP 语言不一样,比如继承 :Golang 没有 extends 关键字,继承是通过匿名字段来实现。

  5. Golang 面向对象(OOP)很优雅,OOP 本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。后面同学们会充分体会到这个特点。也就是说在Golang 中面向接口编程是非常重要的特性。

结构体变量(实例)在内存的布局(重要!)
在这里插入图片描述

2.2 声明、使用及注意细节

①声明

type 结构体名称 struct { 
	field1 type
	field2 type

}
  1. 从概念或叫法上看: 结构体字段 = 属性 = field (即授课中,统一叫字段)
  2. 字段是结构体的一个组成部分,一般是基本数据类型、数组,也可是引用类型。比如我们前面定义猫结构体 的 Name string 就是属性

②使用

package main
import (
	"fmt"
	_ "sort"
)

//首字母大写表明公有
type Person struct {
	Name string
	Age int
	slice []int //切片
	map1 map[string]string //map
}

func main(){
	//1. 直接声明
	var person Person 
	person.slice = make([]int, 5)
	person.map1 = make(map[string]string)
	fmt.Println("person=", person)

	//2. 直接声明并初始化 {}
	var person2 Person = Person{"jack", 20, make([]int, 5), make(map[string]string)}
	fmt.Println("person2=", person2)
	// person3 := Person{"jucy", 0, make([]int, 5), make(map[string]string)}
	// fmt.Println("person3=", person3)

	//3. &
	var p3 *Person = new(Person)
	//因为p3是一个指针, 因此:
	//(*)p3.Name = "smith" 也可以这样写 p3.Name = "smith"
	//go的设计者为了程序员使用方便,所以在底层帮我们做了封装,底层会对 p3.Name = "smith"进行处理,会变为(*p3).Name
	(*p3).Name = "smith"
	p3.Name = "john"
	fmt.Println("person3=", p3)

	//4. {}
	var p4 *Person = &Person{}
	(*p4).Name = "scott"
	p4.Name = "scott~~"
	fmt.Println("person4=",*p4)
}

在这里插入图片描述

③注意细节

  1. 字段声明语法同变量,示例:字段名 字段类型
  2. 字段的类型可以为:基本类型、数组或引用类型
  3. 在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),规则同前面讲的一样:
    布尔类型是 false ,数值是 0 ,字符串是 “”。
    数组类型的默认值和它的元素类型相关,比如 score [3]int 则为[0, 0, 0]
    指针,slice,和 map 的零值都是 nil ,即还没有分配空间。
    在这里插入图片描述
  1. 不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个, 结构体是值类型。

注意:

  1. 结构体的所有字段在内存中是连续的
  2. 结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
  3. 结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转
  4. struct 的每个字段上,可以写上一个 tag, 该 tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化。

序列化使用场景:
在这里插入图片描述

package main
import (
	"fmt"
	"encoding/json"
)

//前后端传输过程中,前端更希望字段名为小写,但是go语言如果为了让其他包能访问到字段只能
//首字母大写,因此就需要struct的tag来完成
type Monster struct {
	Name string `json:"name"` //`json:"name"`就是strcut tag
	Age int `json:"age"`
	Skill string `json:"skill"`
}


func main(){
	monster := Monster{"牛魔王", 500, "芭蕉扇~"}
	//将monster变量序列化为json格式字符串
	//json.Marshal 函数中使用反射
	jsonStr, err := json.Marshal(monster)
	if err != nil {
		fmt.Println("json处理错误 ", err)
	} 
	//jsonStr {"name":"牛魔王","age":500,"skill":"芭蕉扇~"}
	fmt.Println("jsonStr", string(jsonStr))
}

3 方法

3.1 介绍

在某些情况下,我们要需要声明(定义)方法。比如 Person 结构体:除了有一些字段外( 年龄,姓名…),Person 结构体还有一些行为比如:可以说话、跑步…,通过学习,还可以做算术题。这时就要用方法才能完成。

  • Golang 中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型, 都可以有方法,而不仅仅是 struct。

3.2 方法的声明和调用

package main
import (
	"fmt"
)

type Person struct {
	Name string
}

//给Person绑定一个方法[绑定之后只有Person才能使用]
func (p Person) test(){
	fmt.Println("test() name=", p.Name)
}

func main(){
	var p Person
	p.Name = "tom"
	p.test()//调用方法
}
  1. test 方法和 Person 类型绑定
  2. test 方法只能通过 Person 类型的变量来调用,而不能直接调用,也不能使用其它类型变量来调
  3. func (p Person) test() {}… p 表示哪个 Person 变量调用,这个 p 就是它的副本, 这点和函数传参非常相似。
  4. p 这个名字,有程序员指定,不是固定, 比如修改成 person 也是可以

3.3 方法的调用和传参机制(重要)

方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当做实参也传递给方法。下面我们举例说明。

例如:getSum的方法过程
在这里插入图片描述

说明:

  1. 在通过一个变量去调用方法时,其调用机制和函数一样
  2. 不一样的地方时,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果变量是引用类型,则进行地址拷贝)

3.4 注意事项

①声明和定义

func (recevier type) methodName(参数列表) (返回值列表){
方法体
return 返回值
}

  1. 参数列表:表示方法输入
  2. recevier type : 表示这个方法和 type 这个类型进行绑定,或者说该方法作用于 type 类型
  3. receiver type : type 可以是结构体,也可以其它的自定义类型
  4. receiver : 就是 type 类型的一个变量(实例),比如 :Person 结构体 的一个变量(实例)
  5. 返回值列表:表示返回的值,可以多个
  6. 方法主体:表示为了实现某一功能代码块
  7. return 语句不是必须的。

②注意事项和细节

  1. 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
  2. 如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
  3. Golang 中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型, 都可以有方法,而不仅仅是 struct, 比如 int , float32 等都可以有方法
  4. 方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问。
  5. 如果一个类型实现了 String()这个方法,那么 fmt.Println 默认会调用这个变量的 String()进行输出【类比java中重写了toString方法】

3.5 方法和函数的区别

  1. 调用方式不一样
    函数的调用方式: 函数名(实参列表)
    方法的调用方式: 变量.方法名(实参列表)
  2. 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然
  3. 对于方法(如 struct 的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以
package main

import (
	"fmt"
)

type Person struct {
	Name string
}

//对于方法(如:struct的方法)
//接收者为值类型时,可以直接使用指针类型的变量调用方法,反过来也同样可以

//1. 和结构体类型绑定
func (p Person) test03(){
	p.Name = "jack"
	fmt.Println("test03()=", p.Name)
}

//2. 和指针类型绑定
func (p *Person) test04(){
	p.Name = "mary"
	fmt.Println("test04()=", p.Name)
}



func main(){
	var p Person
	p.Name = "tom"
	p.test03()
	fmt.Println("main() p.Name=", p.Name) //tom

	(&p).test03() //从形式上看是传入地址,但是本质上仍然是值拷贝【因为test03方法是和结构体绑定的】
	fmt.Println("main() p.Name=", p.Name) //tom

	(&p).test04()
	fmt.Println("main() p.Name=", p.Name) //mary
	p.test04() //等价(&p).test04(),从形式上是传入值类型,但是本质仍然是地址拷贝
	//【调用方不决定,决定权在调用的方法和什么绑定】

}

总结:

  1. 不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法是和哪个类型绑定.
  2. 如果是和值类型,比如 (p Person) , 则是值拷贝, 如果和指针类型,比如是 (p *Person) 则是地址拷贝。

3.6 工厂模式(类比get、set)

在java中为了访问私有的属性,通常会提供对应的get、set方法,但是Go中并没有;因此我们可以使用工厂模式来达到类似的效果

  • Golang 的结构体没有构造函数,通常可以使用工厂模式来解决这个问题。
一个结构体的声明是这样的: 
package model
type Student struct { 
	Name string...
}

因为这里的Student 的首字母S 是大写的,如果我们想在其它包创建 Student 的实例(比如main 包), 引入 model 包后,就可以直接创建 Student 结构体的变量(实例)。但是问题来了,如果首字母是小写的, 比如 是 type student struct {…} 就不不行了,怎么办—> 工厂模式来解决.

  1. model包中定义一个结构体student

main.go:

package model

//定义一个私有的结构体
//Name字段是共有的,age字段是私有的
type student struct {
	Name string
	age int
}

//工厂模式:类比构造函数
//返回Student指针类型
func NewStudent(name string, age int) *student{
	return &student{
		Name: name,
		age: age,
	}
}

//工厂模式定义student对应的方法:类比get、set方法
func (stu *student) GetAge() int{
	return (*stu).age
}

func (stu *student) SetAge (age int) {
	(*stu).age = age
}
  1. 在main包下使用student

hello.go

package main

import (
	"fmt"
	"go_code/project01/main/model"
)

func main(){
	//创建一个student
	var stu = model.NewStudent("tom", 20)
	stu.SetAge(17)
	//因为Name在model中是首字母大写表明是公有的,因此可以直接访问;而age需要通过GetAge函数实现
	//tom 17
	fmt.Println(stu.Name, stu.GetAge())
}

在这里插入图片描述

4 面向对象

4.1 面向对象练习题(封装)

  1. 创建程序,在 model 包中定义 Account 结构体:在 main 函数中体会 Golang 的封装性。
  2. Account 结构体要求具有字段:账号(长度在 6-10 之间)、余额(必须>20)、密码(必须是六
  3. 通过 SetXxx 的方法给 Account 的字段赋值。(同学们自己完成
  4. 在 main 函数中测试
  1. 定义account结构体
package model

import (
	"fmt"
)

type account struct {
	accountNo string //账户
	Balance float64	//余额
	Password string
}

//使用工厂方法完成构造
func NewAccount(accountNo string, Balance float64, Password string) *account {
	if len(accountNo) < 6 || len(accountNo) > 10 {
		fmt.Println("账号必须在6-10之间...")
		return nil
	}
	if Balance < 20 {
		fmt.Println("余额不能少于20...")
		return nil
	}
	if len(Password) != 6 {
		fmt.Println("密码必须是6位..")
		return nil
	}
	account := account{
		accountNo: accountNo,
		Balance: Balance,
		Password: Password,
	}
	fmt.Println("创建账户成功~~~")
	return &account
}

//定义get方法[与结构体绑定]
func (a account) GetAccountNo() string{
	return a.accountNo
}

//set方法[与指针绑定]
func (a *account) SetAccountNo(accountNo string){
	(*a).accountNo = accountNo
}
  1. 使用
package main

import (
	"fmt"
	"go_code/project01/main/model"
)

func main(){
	//创建一个账户
	var account = model.NewAccount("4131341324", 35.5, "348291")
	fmt.Println("账户No=", account.GetAccountNo())
	account.SetAccountNo("6666666666")
	fmt.Println("新的账户No=", account.GetAccountNo())
}

结果:
在这里插入图片描述

4.2 面向对象(继承)

①概念

在这里插入图片描述

继承的优点:

  1. 代码的复用性提高了
  2. 代码的扩展性和维护性提高了
type Goods struct { Name string Price int
}
type Book struct {
//这里就是嵌套匿名结构体 Goods Writer string
	Goods	
}

②使用及注意点

继承的深入讨论:

  1. 结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段、方法, 都可以使用。【举例说明】
package main

import (
	"fmt"
	_ "go_code/project01/main/model"
)

type A struct {
	Name string
	age int
}

//公有
func (a *A) SayOk(){
	fmt.Println("A SayOk ", a.Name)
}

//私有
func (a *A) hello(){
	fmt.Println("A hello", a.Name)
}


type B struct {
	//B继承了A
	A
}

func main(){
	var b B
	b.A.Name = "tom"
	//b.Name = "jack" //两种写法均可
	b.age = 12
	b.SayOk()
	b.hello()
}
  1. 匿名结构体字段访问可以简化
	b.A.Name = "tom"
	//b.Name = "jack" //两种写法均可

对上面的代码小结
(1) 当我们直接通过 b 访问字段或方法时,其执行流程如下比如 b.Name
(2) 编译器会先看 b 对应的类型有没有 Name, 如果有,则直接调用 B 类型的 Name 字段
(3) 如果没有就去看 B 中嵌入的匿名结构体 A 有没有声明 Name 字段,如果有就调用,如果没有继续查找…如果都找不到就报错.

  1. 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分【举例说明】
    在这里插入图片描述
  2. 结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错。【举例说明】
    在这里插入图片描述
  3. 如果一个 struct 嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字
    在这里插入图片描述
    在这里插入图片描述
  4. 嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
package main

import (
	"fmt"
	_ "go_code/project01/main/model"
)

type Goods struct {
	Name string
	Price float64
}

type Brand struct {
	Name string
	Address string
}

type TV struct {
	Goods
	Brand
}

type TV2 struct {
	*Goods
	*Brand
}


func main(){
	//嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值
	tv := TV{Goods{"电视机", 5000.99}, Brand{"海尔", "山东"}}
	tv2 := TV{
		Goods{
			Price: 6999.99,
			Name: "电视机002",
		},
		Brand{
			Name: "夏普",
			Address: "北京",
		},
	}
	fmt.Println("tv=", tv)
	fmt.Println("tv2=", tv2)

	tv3 := TV2{
		&Goods{
			Name: "电视机003",
			Price: 79999.99,
		},
		&Brand{
			Name: "长虹",
			Address: "四川",
		},
	}
	fmt.Println("tv3=", *(tv3.Goods), *(tv3.Brand))
}

在这里插入图片描述7. 结构体的匿名字段是基本数据类型,如果需要有多个 int 的字段,则必须给 int 字段指定名字

package main

import (
	"fmt"
	_ "go_code/project01/main/model"
)

type Monster struct {
	Name string
	Age int
}

type E struct {
	Monster
	int //匿名字段的基本数据类型
	n int
}


func main(){
	//匿名字段是基本数据类型的使用
	var e E
	e.Name = "狐狸精"
	e.Age = 300
	e.int = 20
	e.n = -50
	//e= {{狐狸精 300} 20 -50}
	fmt.Println("e=",e)

}

③多重继承

如一个 struct 嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承。

在这里插入图片描述

多重继承细节说明

  1. 如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来区分。【案例演示】
    在这里插入图片描述

  2. 为了保证代码的简洁性,建议大家尽量不使用多重继承

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/485450.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OK6410A 中的 irqdomain 之 gpio

文章目录 VIC domain 与 gpio domain 的硬件拓扑语言描述VIC 与 INT_EINTx 的关系INT_EINTx 与 GPIO的关系INT_EINT0INT_EINT1INT_EINT2INT_EINT3INT_EINT4INT_EINT4 与 External interrupt Group1-9 的关系External interrupt Group1External interrupt Group2External interr…

基于Open3D的点云处理2-Open3D的IO与数据转换

三维数据类型 点云 某个坐标系下的点数据集&#xff0c;每个点包括三维坐标X&#xff0c;Y&#xff0c;Z、颜色、分类值、强度值、时间等信息&#xff1b; 储存格式&#xff1a;pts、LAS、PCD、xyz、asc、ply等&#xff1b;Mesh 多边形网格&#xff0c;常见的是三角网格&#…

node快速搭建接口实现登录退出,增删改查功能供前端使用,结尾有完整代码

用node快速搭建接口 环境实现功能具体实现步奏数据库设计用express创建一个服务器实例创建数据库连接池配置跨域请求和解析前端数据登录接口实现验证token的中间件退出接口获取用户信息接口增删改查功能 完整代码 环境 node版本v17.0.0 所用到的依赖 "dependencies"…

量子力学奇妙之旅-从相对论下薛定谔方程到量子场论

专栏目录: 高质量文章导航-持续更新中 1.用于描述一个多粒子体系的波函数 前置:最小的尺度 由于不确定性原理 因此为了测量到更小的空间尺度,我们就需要提高探测粒子的动量

RSA非对称加密(附工具类Util)

文章目录 非对称加密是什么非对称加密通信流程RSA非对称加密算法非对称加密工具类Util及案例演示 之前写过一篇关于DES对称加密的帖子&#xff0c;感兴趣的小伙伴可以去看看&#xff1a;DES对称加密算法 今天主要聊聊什么是非对称加密&#xff0c;以及它是如何实现的。 一、非…

f(x)与|f(x)|,f ‘ (x),F(x)常见关系。

1.f(x)与|f(x)|关系。 1.连续关系。(f(x)在"[a,b]上连续" > |f(x)|在"[a,b]连续") ①如果f(x)在[a,b]上连续。则|f(x)|在[a,b]上连续. &#xff08;因为f(x)在x0的连续点>x0必为|f(x)|的连续点&#xff09; 注&#xff1a;”[a,b]连续“包括&#…

PUBG绝地求生的制作公司计划今年推出NFT元宇宙游戏

欢迎来到Hubbleverse &#x1f30d; 关注我们 关注宇宙新鲜事 &#x1f4cc; 预计阅读时长&#xff1a;5分钟 本文仅代表作者个人观点&#xff0c;不代表平台意见&#xff0c;不构成投资建议。 近年来&#xff0c;元宇宙的概念在游戏界获得了极大的关注。元宇宙指的是一个虚…

武汉大学惯性导航课程合集【2021年秋】2.1 惯导机械编排算法

Vrb是客观存在的&#xff0c;b相对于r的速度&#xff08;从R到b的变化&#xff09;&#xff0c;右上角的b表示投影到p坐标系&#xff0c;只是表达数值的不同。 &#xff08;工程上5-10&#xff0c;50倍&#xff09;奈奎斯特采样率&#xff0c;香农采样定理&#xff0c;又称奈…

Synthesys:语音合成和视频生成平台

【产品介绍】 Synthesys是一个基于人工智能的语音合成和视频生成平台&#xff0c;可以让你用几分钟的时间&#xff0c;就能制作出专业的音频和视频内容&#xff0c;无需花费大量的金钱和时间去雇佣演员、摄像机或音频设备。Synthesys的技术可以把你的文本转换成逼真的人声和人像…

《花雕学AI》AI时代来临,互联网教父凯文·凯利给你50条生存指南:5000天后的世界会是什么样?

你知道凯文凯利吗&#xff1f;他是《连线》杂志的创始人之一&#xff0c;被誉为“世界互联网教父”&#xff0c;他的预言和观点影响了无数人的思考和行动。他曾经预言过互联网、社交媒体、区块链等技术的发展和变革&#xff0c;而现在&#xff0c;他又给我们带来了一个全新的预…

Vue电商项目--防抖节流应用

演示卡顿现象 正常&#xff1a;事件触发非常频繁&#xff0c;而且每一次的触发&#xff0c;回调函数都要去执行&#xff08;如果时间很短&#xff0c;而回调函数内部有计算&#xff0c;那么很容易出现浏览器卡顿&#xff09; 正常情况下&#xff08;用户慢慢的操作&#xff0…

基于Transformer的DETR的注意力权重可视化,非CAM可视化技术

神经网络的可视化可以客观的解释 “黑盒” &#xff0c;所以一直以来都是论文中必不可少的工作。对于深度卷积神经网络&#xff0c;一般用CAM进行可视化研究。遗憾的是&#xff0c;基于Transformer的神经网络可视化&#xff0c;CAM并不奏效。所以&#xff0c;本文章提供一套基于…

考研机试刷题第二天:任意进制转任意进制【高进度短除法】

理一下思路&#xff1a; 看了y总的视频之后我觉得这道题其实只需要对上次写的进制转换微微做一下调整即可。 于是我写出了下面的代码 #include <iostream> #include <vector> #include <algorithm> #include <cstring>using namespace std;vector<…

SpringCloud微服务如何进行权限校验?

一、概述&#xff1a; 1、什么是Spring Cloud&#xff1f; SpringCloud, 基于SpringBoot提供了一套微服务解决方案&#xff0c;包括服务注册与发现&#xff0c;配置中心&#xff0c;全链路监控&#xff0c;服务网关&#xff0c;负载均衡&#xff0c;熔断器等组件&#xff0c;…

数据结构(六)—— 二叉树(4)回溯

文章目录 一、题1 257 二叉树的所有路径1.1 写法11.2 写法2 一、题 1 257 二叉树的所有路径 1.1 写法1 递归回溯&#xff1a;回溯是递归的副产品&#xff0c;只要有递归就会有回溯 首先考虑深度优先搜索&#xff1b;而题目要求从根节点到叶子的路径&#xff0c;所以需要前序…

第一章 算法概述

第1章-算法概述 总分&#xff1a;100分 得分&#xff1a;30.0分 1 . 填空题 简单 10分 递归算法必须具备的两个条件是___和___ 回答错误 答案 边界条件或停止条件、递推方程或递归方程 2 . 填空题 中等 10分 冒泡排序时间复杂度是___&#xff0c;堆排序时…

深度学习笔记--本地部署Mini-GPT4

目录 1--前言 2--配置环境依赖 3--下载权重 4--生成 Vicuna 权重 5--测试 6--可能出现的问题 1--前言 本机环境&#xff1a; System: Ubuntu 18.04 GPU: Tesla V100 (32G) CUDA: 10.0 项目地址&#xff1a;https://github.com/Vision-CAIR/MiniGPT-4 2--配置环境依赖 …

18.考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化

说明书 MATLAB代码&#xff1a;考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化 关键词&#xff1a;碳交易 电制氢 阶梯式碳交易 综合能源系统 热电优化 参考文档&#xff1a;《考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化》基本复现 仿真平台&#xff1a;M…

洞车常见问题解决指南

洞车常见问题解决指南 1.研发脚本处理问题1.1 WMS出库单无法审核1.2 OMS入库单无法提交&#xff0c;提示更新中心库存失败1.3 当出现OMS下发成功WMS/TMS/DMS还没有任务的情况时处理方案1.4 调度波次生成或者添加任务系统异常1.5 东鹏出库单部分出库回传之后要求重传1.6 更新订单…

表情符号(emoji)大全,只此一文便够了

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 表情符号&#xff08;emoji&#xff09;大全、只此一文便够了 摘要集中展示笑脸和动物人庆贺和物品食品和物交通和地点符号 符号表smileys_and_peopleanimals_and_naturefood_and_dr…