struct

作者: JunChow520 | 来源:发表于2022-01-03 23:02 被阅读0次

面向对象编程

  • Go支持面向对象编程特性,但和传统面向对象编程是有区别的,Go并不是纯粹的面向对象语言。
  • Go没有类,其结构体和其它编程语言中的类具有同等的地位,可理解Go是基于结构体来实现面向对象特性的。
  • Go面向对象编程非常简介,去掉了传统面向对象语言的继承、方法重载、构造函数、析构函数、隐藏的this指针等。
  • Go仍然具有面向对象编程的继承、封装、多态的特性,只是实现方式和传统面向对象语言不同。
  • Go面向对象很优雅,面向对象本身就是语言类型系统的一部分,通过接口关联,耦合性低,也非常灵活。
  • Go中面向接口编程是非常重要的特性

结构体与结构体变量

  • 结构体:自定义的数据类型,表示一类事物。
type struct_name struct {
  field1 datatype
  field2 datatype
}

//定义结构体
type User struct {
    Id     int
    Name   string
    Birth  string
    Gender int
}
  • 结构体变量:又称为实例或对象,是具体的、实际的,代表一个具体变量。

//结构体变量
var u User
u.Id = 1
u.Name = "alice"
u.Birth = "2020-12-21"
u.Gender = 1

结构体字段

  • 结构体字段又称为属性,字段是结构体组成部分,一般是基础数据类型、数组,也可以是引用类型。
  • 字段声明语法同变量一样采用字段名 字段类型
  • 字段类型可分为:基本类型、数组、引用类型
  • 创建结构体变量后若没有给字段赋值,都会对应一个零值作为默认值。
  • 不同结构体变量的字段是独立互不影响,一个结构体变量的更改不会影响到另外一个,因为结构体是值类型默认是值拷贝。
字段类型 类型 零值
布尔类型 基础 false
整型 基础 0
字符串 基础 ""
数组类型 基础 默认值与元素类型相关
指针ptr/切片slice/映射map 引用 nil,未分配空间。

结构体字段类型若存在指针、slicemap,它们的零值都是nil,即还没有分配空间,如果需要使用必须先make()

类型 创建
指针 new()
切片 make()
映射 make()
//定义结构体
type User struct {
    Id    int
    array [5]float64
    slice []int
    ptr   *int
    dict  map[string]string
}

//结构体变量
var u User
fmt.Printf("%+v\n", u)             // {Id:0 array:[0 0 0 0 0] slice:[] ptr:<nil> dict:map[]}
fmt.Printf("%t\n", u.slice == nil) //true
fmt.Printf("%t\n", u.ptr == nil)   //true
fmt.Printf("%t\n", u.dict == nil)  //true

创建结构体变量

//创建结构体变量
var u1 User             //直接声明
fmt.Printf("%+v\n", u1) //{Id:0}

var u2 User = User{}
fmt.Printf("%+v\n", u2) //{Id:0}

采用结构体指针方式创建后,结构体指针访问字段的标准方式应该是 (*结构体指针).字段名,但Go做了简化也支持结构体指针.字段名,因为这种方式更加符合程序员的使用习惯。Go编译器底层会对简化方式做自动转换。

var u3 *User = new(User)
fmt.Printf("%+v\n", u3) //&{Id:0}

var u4 *User = &User{}
fmt.Printf("%+v\n", u4) //&{Id:0}

简化写法

var u *User = new(User)
fmt.Printf("%+v\n", u) //&{Id:0}

(*u).Id = 100
fmt.Printf("%+v\n", *u)

u.Id = 200
fmt.Println(u)

由于u是一个指针,因此标准的给字段赋值的方式应该采用的是(*u).Id = 100,但也可以直接采用u.Id = 200。由于Go设计者为了程序员使用方便,底层会对u.Id = 200添加取值运算。

创建结构体实例时可直接指定字段的值

type User struct {
    Id   int
    Name string
}

func main() {
    u := User{1, "alice"}
    fmt.Printf("%+v\n", u) // {Id:1 Name:alice}
}

内存分配机制

例如:结构体是值类型,默认赋值会拷贝副本,若需采用引用类型方式,则需使用结构体变量指针赋值。

u1 := User{1}
u2 := &u1 // var u2 *User = &u1
u2.Id = 100
fmt.Printf("%+v\n", u1)               // {Id:100}
fmt.Printf("%+v\n", u2)               // &{Id:100}
fmt.Printf("u1=%p, u2=%p\n", &u1, u2) // u1=0xc0000aa058, u2=0xc0000aa058

.点运算符优先级要高于*星号运算符,下列为错误写法:

u1 := User{1}
u2 := &u1 // var u2 *User = &u1
u2.Id = 100
fmt.Printf("%+v\n", *u2.Id) // invalid indirect of u2.Id (type int) (exit status 2)

注意事项

结构体字段在内存中是连续分布的

type Point struct {
    x, y int
}
type Rect struct {
    leftUp, rightDown Point
}

func main() {
    r := Rect{
        Point{0, 0},
        Point{4, 4},
    }
    fmt.Printf("%p\n", &r.leftUp.x)    // 0xc0000a8080
    fmt.Printf("%p\n", &r.leftUp.y)    // 0xc0000a8088
    fmt.Printf("%p\n", &r.rightDown.x) // 0xc0000a8090
    fmt.Printf("%p\n", &r.rightDown.y) // 0xc0000a8098
}

指针本身的地址是连续但指向的地址不一定是连续的

type Point struct {
    x, y int
}
type Rect struct {
    leftUp, rightDown *Point
}

func main() {
    r := Rect{
        &Point{0, 0},
        &Point{4, 4},
    }
    fmt.Printf("%p\n", &r.leftUp)    // 0xc000088220
    fmt.Printf("%p\n", &r.rightDown) // 0xc000088228

    fmt.Printf("%p\n", r.leftUp)    // 0xc0000aa070
    fmt.Printf("%p\n", r.rightDown) // 0xc0000aa080

}

结构体是用户单独定义的类型,和其它类型进行转换时需具有完全相同的字段(名字、个数、类型)。

type Point struct {
    x, y int
}
type Axis struct {
    x, y int
}

func main() {
    p1 := Point{0, 0}
    p2 := Axis(p1)
    fmt.Printf("%+v\n", p1) // {x:0 y:0}
    fmt.Printf("%+v\n", p2) // {x:0 y:0}
}

结构体进行type重新定义时相当于取别名,Go会认为是新的数据类型,但相互之间可以强转。

type Point struct {
    x, y int
}
type Axis Point

func main() {
    var p1 Point
    var p2 Axis

    //p1 = p2 //  cannot use p2 (type Axis) as type Point in assignment (exit status 2)

    p1 = Point(p2)
    fmt.Printf("%+v %+v\n", p1, p2) // {x:0 y:0} {x:0 y:0}
}

结构体字段上可添加一个标签tag,标签可通过反射机制获取,比如序列化和反序列化中。

type Response struct {
    Code int         `json:"code"`
    Msg  string      `json:"msg`
    Data interface{} `json:"data"`
}

func main() {
    response := Response{200, "success", nil}
    buf, err := json.Marshal(response)
    if err != nil {
        log.Fatalln(err)
    }
    fmt.Printf("%s\n", buf) // {"code":200,"Msg":"success","data":null}
}

工厂模式

Go的结构体没有构造函数,可通过工厂模式来实例化结构体。

// 结构体
type user struct {
    Id   int
    Name string
}

// 工厂模式
func User(id int, name string) *user {
    return &user{
        Id:   id,
        Name: name,
    }
}

func main() {
    u := User(1, "alice")
    fmt.Printf("%+v\n", u) // &{Id:1 Name:alice}
}

相关文章

网友评论

      本文标题:struct

      本文链接:https://www.haomeiwen.com/subject/vuqfcrtx.html