GO语言学习笔记---结构体

文章目录

  • 结构体
    • 定义结构体
    • 实例化结构体---为结构体分配内存并初始化
    • 初始化结构体成员变量
    • 构造函数---结构体和类型的一系列初始化操作的函数封装
    • 方法
      • 接收器---方法作用的目标
      • 为类型添加方法
    • 类型内嵌和结构体内嵌
      • 结构体内嵌特性
      • 使用组合思想描述对象特性
      • 初始化结构体内嵌
      • 初始化内嵌匿名结构体
      • 成员名字冲突

结构体

Go语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性。
结构体成员是由一系列的成员变量构成,这些成员变量也被称为字段。字段有一下特性:

  • 拥有自己的类型和值
  • 字段名必须唯一
  • 字段的类型也可以是结构体,甚至是字段所在结构体的类型。

注意:Go语言中没有类的概念,也不支持类的继承等面向对象的概念。

定义结构体

定义格式如下:

type 类型名 struct{
字段1 字段1 类型
...
//例如
type Point struct{
X int
Y int
}
}

实例化结构体—为结构体分配内存并初始化

实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,各个实例间的内存是完全独立的。
1.基本实例化的形式

//ins为结构体实例名称,T为结构体类型
var ins T

2.创建指针类型的结构体

ins := new(T)

注意:

  • new实例化的结构体返回的是指针,但是和普通的实例化的用法一致。
  • 这点和C++不同,C++中指针访问成员必须使用"->"操作符。
  • Go之所以不同是因为使用了语法糖(Syntactic sugar)

3.取结构体的地址实例化
取地址格式如下:

ins :=&T{}

//示例
type Command struct{
Name string
Var   *int
Comment string
}
var version int =1
cmd :=&Command{}
cmd.Name="version"
cmd.Var=&version
cmd.Comment="show version"

初始化结构体成员变量

1**.键值对初始化;**

type Command struct{
Name string
Var   *int
Comment string
}
cmd :=&Command{
Name:"version",
Var : 1,
Comment: "show version",
}

2.使用多个值的列表初始化结构体

type Command struct{
Name string
Var   *int
Comment string
}
cmd :=Command{
	"version"
	1
	"show version"
}

3.初始化匿名结构体

//定义部分
msg :=struct{
ind int
data string
}{				//值初始化部分
	"1024"
	"hello"
}

构造函数—结构体和类型的一系列初始化操作的函数封装

Go语言没有构造函数的功能。结构体的初始化过程可以使用函数封装实现。
使用结构体描述猫的特性,根据猫的颜色和名字就可以有不同的种类的猫。就可以创造不同的实例

type Cat struct{
	Color string
	Name string
	
}
func NewCatByName(name string) *Cat{
	return &Cat{
		Name:name,
	}
}
func NewCatByName(color string) *Cat{
	return &Cat{
		Color:color
	}
}

实现类似于父子类关系的派生:
黑猫是猫,但是猫是一种猫的泛称。在C++语言中,猫就是基类,黑猫就是猫的派生类。那么Go语言中怎样实现?

type Cat struct{
	Color string
	Name string
}
type BlackCat struct{
	Cat				//嵌入Cat,类似于派生
}
//构造基类
func NewCat(name string)* Cat{
	return &Cat{
		Name:name
	}
//模拟构造子类
func NewBlackCat(color string)* BlackCat{
	cat :=&BlackCat()
	cat,color=color
	return cat
}
}

方法

Go语言中的方法是一种作用于特定类型变量的函数。这种特定类型变量叫做接收器
在go语言中接收器的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。
1.Go语言的结构体方法

type Bag struct{
	items []int
}
func (b *Bag) Insert(itemid int){
	b.items =append(b.items,itemid)
}
func main(){
	b:=new(Bag)
	b.Insert(1001)
}

接收器—方法作用的目标

格式如下:

func (接收器变量 接收器类型) 方法名(参数列表) (返回参数){
}

类型:指针接收器、非指针接收器。两种接收器会被用于不同性能和功能要求的代码中。

1.指针类型的接收器。

指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的this或者self.
由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。

我的理解是其用法很像是将类换成了结构体,然后接口就是面向对象中的方法。

示例代码:

package main

import (
	"fmt"
)

//定义属性结构
type Property struct {
	value int //属性值
}

//设置属性值
func (p *Property) SetValue(v int) {
	//修改p的成员变量
	p.value = v
}

//取属性值
func (p *Property) Value() int {
	return p.value
}
func main() {
	//实例化属性
	p := new(Property)

	//设置值
	p.SetValue(100)

	//打印值
	fmt.Println(p.Value())
}

输出:100

2.理解非指针类型的接收器
当方法作用于非指针接收器时,Go语言会在代码运行时将接收器的值复制一份。在非指针接收器的方法中可以获取接收器的成员值,但修改后无效

示例代码:

package main

import (
	"fmt"
)

//定义点结构
type Point struct {
	X int
	Y int
}

//非指针接收器的加方法
func (p Point) Add(other Point) Point {
	//成员值与参数相加后返回新的结构
	return Point{p.X + other.X, p.Y + other.Y}
}
func main() {
	//初始化点
	p1 := Point{1, 1}
	p2 := Point{2, 2}

	//与另外一个点相加
	result := p1.Add(p2)

	//输出结果
	fmt.Println(result)
}

输出:{3,3}

为类型添加方法

Go语言可以对任何类型添加方法,给一种类型添加方法就像给结构体添加方法一样,因为结构体也是一种类型。

1.为基本类型添加方法:

package main

import (
	"fmt"
)

//将int定义为MyInt类型
type MyInt int

//为MyInt添加IsZero方法
func (m MyInt) IsZero() bool {
	return m == 0
}

//为MyInt添加Add()方法
func (m MyInt) Add(other int) int {
	return other + int(m)
}

func main() {
	var b MyInt
	fmt.Println(b.IsZero())
	b = 1
	fmt.Println(b.Add(2))
}

输出:
在这里插入图片描述

2.http包中的类型方法
Go语言提供的http包里也大量使用了类型方法。
下面创建一个HTTP请求,并且设定HTTP头。

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
)

func main() {
	client := &http.Client{}
	//创建一个HTTP请求
	req, err := http.NewRequest("POST", "http://www.163.com", strings.NewReader("key=value"))

	//发现错误就打印退出
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
		return
	}
	//为标头添加信息
	req.Header.Add("User-Agent", "myClient")

	//开始请求
	resp, err := client.Do(req)

	//错误处理
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
		return
	}

	//读取服务器返回的内容
	data, err := ioutil.ReadAll(resp.Body)
	fmt.Println(string(data))
	defer resp.Body.Close()
}

输出:
GO语言学习笔记---结构体_第1张图片
在这个例子中,req.Header就是典型的自定义类型。
定义类型如下:

type Header map[string] []string

func (h Header) Add (key,value string){
	textproto.MIMEHeader(h).Add(key,value)
}

类型内嵌和结构体内嵌

结构体允许其成员在声明时没有字段名而只有类型,这种形式的字段被称为类型内嵌或匿名字段。
1.类型内嵌:同种类型的匿名字段只能有一个。
写法如下:

type Data struct{
	int 
	float32
	bool
}

ins := &Data{
	int: 10,
	float32: 3.14,
	bool: true,
}

2.结构体内嵌
使用结构体内嵌处理计算机图形学的颜色和透明度表示:

package main

import (
	"fmt"
)

type BasicColor struct {
	R, G, B float32
}
type Color struct {
	BasicColor
	Alpha float32
}

func main() {
	var c Color
	c.R = 1
	c.G = 1
	c.B = 1

	c.Alpha = 1

	fmt.Printf("%+v", c)
}

输出:在这里插入图片描述

结构体内嵌特性

1.内嵌的结构体可以直接访问其成员变量
2.内嵌结构体的字段名是它的类型名

使用组合思想描述对象特性

在面向对象思想中,实现对象关系需要使用"继承特性"。如,人类不能飞行,鸟可以飞行。人类和鸟类都可以继承自行走类,但只有鸟类继承自飞行类。

Go语言的结构体内嵌实现对象特性组合。
也可以理解为Go语言的面向对象中的继承实现方式

package main

import (
	"fmt"
)

//可飞行的
type Flying struct {
}

func (f *Flying) Fly() {
	fmt.Println("can fly")
}

//可行走的
type Walkable struct {
}

func (f *Walkable) Walk() {
	fmt.Println("can walk")
}

//人类
type Human struct {
	Walkable
}

//鸟
type Bird struct {
	Walkable
	Flying
}

func main() {
	//实例化鸟类
	b := new(Bird)
	fmt.Println("Bird:")
	b.Fly()
	b.Walk()
	fmt.Println("---------------")
	//实例化人类
	h := new(Human)
	fmt.Println("Human:")
	h.Walk()
}

输出:
GO语言学习笔记---结构体_第2张图片

初始化结构体内嵌

package main

import (
	"fmt"
)

//车轮
type Wheel struct {
	Size int
}

//引擎
type Engine struct {
	Power int    //功率
	Type  string //类型
}

//车
type Car struct {
	Wheel
	Engine
}

func main() {
	c := Car{
		//初始化轮子
		Wheel: Wheel{
			Size: 18,
		},
		Engine: Engine{
			Type:  "1.4T",
			Power: 143,
		},
	}
	fmt.Printf("%+v\n", c)
}

输出:
在这里插入图片描述

初始化内嵌匿名结构体

package main

import (
	"fmt"
)

//车轮
type Wheel struct {
	Size int
}

//车
type Car struct {
	Wheel
	Engine struct {
		Power int    //功率
		Type  string //类型
	}
}

func main() {
	c := Car{
		//初始化轮子
		Wheel: Wheel{
			Size: 18,
		},
		Engine: struct {
			Power int
			Type  string
		}{
			Type:  "1.4T",
			Power: 143,
		},
	}
	fmt.Printf("%+v\n", c)
}

成员名字冲突

如果嵌入结构体内部可能拥有相同的成员名,成员重名时编译器会报错。如:
现在有结构体如下:

package main

import (
	"fmt"
)

type A struct {
	a int
}

type B struct {
	a int
}

type C struct {
	A
	B
}

func main() {
	c := &C{}
	c.a = 1
	fmt.Println(c)
}

编译器会报错。将c.a改成c.A.a=1之后,会输出结果。因为如果像前者那样使用,编译器分不清到底将1赋给C中A的a还是B的a。

你可能感兴趣的:(计算机语言---go语言)