1. 程式人生 > 其它 >Go語言自定義型別

Go語言自定義型別

Go語言與C/C++類似,C++可通過typedef關鍵字自定義資料型別(別名、定義結構體等),Go語言則通過type關鍵字可實現自定義型別的實現

1、自定義型別格式

使用者自定義型別使用type,其語法格式為:

type newType oldType

oldType可以是自定義型別、預宣告型別、未命名型別中的任意一種
newType是新型別識別符號,與oldType具有相同的底層型別,並且都繼承了底層型別的操作集合(如底層型別是map,支援range迭代訪問,則新型別也可以使用range迭代訪問)。此外,newTypeoldType是兩個完全不同的型別,newType不會繼承oldType

的方法。

關於newType和oldType是兩個完全不同的的型別?
在Go中分為命名型別和未命名型別
命名型別:通過識別符號符來表示。Go 語言允許使用者定義型別。當用戶宣告一個新型別時,這個宣告就給編譯器提供了一個框架,告知必要的記憶體大小和表示資訊。聲明後的型別與內建型別的運作方式類似。 Go 語言裡宣告使用者定義的型別有兩種方法。最常用的方法是使用關鍵字 struct,它可以讓使用者建立一個結構型別。
未命名型別:一個型別由預宣告型別、關鍵字和操作符組合而成。未命名型別又稱為型別字面量(Type Literal)。Go 語言的基本型別中的複合型別:陣列(array)、切片(slice)、字典(map)、通道(channel)、指標(pointer)、函式字面量(function)、結構(struct)和介面(interface)都屬於型別字面量,也都是未命名型別。所以 *int、[]int、[2]int、map[k]v 都是未命名型別。注意:用 type 宣告的結構和介面是命名型別。

舉例:

type INT int //INT 是一個使用預宣告型別宣告的自定義型別
type Map map[string]string //Map 是一個使用型別字面量宣告的自定義型別
type myMap Map //myMap 是一個自定義型別Map 宣告的自定義型別
// INT, Map 、myMap 都是命名型別

2、自定義struct型別

定義:

type identifier struct {
field type1
field type2
}

2.1 初始化

方式一:通過var宣告結構體
在 Go 語言中當一個變數被宣告的時候,系統會自動初始化它的預設值,比如 int 被初始化為 0,指標為 nil。
var 宣告同樣也會為結構體型別的資料分配記憶體,所以我們才能像上一段程式碼中那樣,在聲明瞭 var s T

之後就能直接給他的欄位進行賦值
方式二:使用new
使用new函式給一個新的結構體變數分配記憶體,它返回指向已分配記憶體的指標:var t *T = new(T)

type struct1 struct {
    i1 int
    f1 float32
    str string
}

func main() {
    ms := new(struct1)
    ms.i1 = 10
    ms.f1 = 15.5
    ms.str= "Chris"

    fmt.Printf("The int is: %d\n", ms.i1)
    fmt.Printf("The float is: %f\n", ms.f1)
    fmt.Printf("The string is: %s\n", ms.str)
    fmt.Println(ms)
}

與面嚮物件語言相同,使用點操作符可以給欄位賦值:structname.fieldname = value
同樣的,使用點操作符可以獲取結構體欄位的值:structname.fieldname
方式三:使用字面量

type Person struct {
    name string
    age int
    address string
}

func main() {
    var p1 Person
    p1 = Person{"lisi", 30, "shanghai"}   //方式A
    p2 := Person{address:"beijing", age:25, name:"wangwu"} //方式B
    p3 := Person{address:"NewYork"} //方式C
}

在(方式A)中,值必須以欄位在結構體定義時的順序給出。(方式B)是在值前面加上了欄位名和冒號,這種方式下值的順序不必一致,並且某些欄位還可以被忽略掉,就想(方式C)那樣。
除了上面這三種方式外,還有一種初始化結構體實體更簡短和常用的方式,如下:

ms := &Person{"name", 20, "bj"}
ms2 := &Person{name:"zhangsan"}

&Person{a, b, c}是一種簡寫,底層仍會呼叫new(),這裡值的順序必須按照欄位順序來寫,同樣它也可以使用在值前面加上欄位名和冒號的寫法(見上文的方式B,C)。

表示式 new(Type) 和 &Type{} 是等價的。

2.2、幾種初始化方式之間的區別

上述瞭解了Go語言中的三種初始化結構體的方式:

//第一種,在Go語言中,可以直接以 var 的方式宣告結構體即可完成例項化
var t T
t.a = 1
t.b = 2

//第二種,使用 new() 例項化
t := new(T)

//第三種,使用字面量初始化
t := T{a, b}
t := &T{} //等效於 new(T)

使用var t T會給t分配記憶體,並零值化記憶體,但這個時候的t的型別是T
使用new 關鍵字時,t := new(T),變數t則是一個指向T的指標
從記憶體佈局上來看,對三種初始化方式的區別:

使用var宣告:

使用new初始化:

使用結構體字面量初始化:

舉例:

package main
import "fmt"

type Person struct {
 name string
 age int
}

func main() {
 var p1 Person
 p1.name = "zhangsan"
 p1.age = 18
 fmt.Printf("This is %s, %d years old\n", p1.name, p1.age)

 p2 := new(Person)
 p2.name = "lisi"
 p2.age = 20
 (*p2).age = 23 //這種寫法也是合法的
 fmt.Printf("This is %s, %d years old\n", p2.name, p2.age)

 p3 := Person{"wangwu", 25}
 fmt.Printf("This is %s, %d years old\n", p3.name, p3.age)
}

輸出:

This is zhangsan, 18 years old
This is lisi, 23 years old
This is wangwu, 25 years old

面例子的第二種情況,雖然 p2 是指標型別,但我們仍然可以像 p2.age = 23 這樣賦值,不需要像 C++ 中那樣使用 -> 操作符,Go 會自動進行轉換。
注意也可以先通過 * 操作符來獲取指標所指向的內容,再進行賦值:(*p2).age = 23

2.3 結構體的記憶體佈局

Go 語言中,結構體和它所包含的資料在記憶體中是以連續塊的形式存在的,即使結構體中巢狀有其他的結構體,這在效能上帶來了很大的優勢。不像 Java 中的引用型別,一個物件和它裡面包含的物件可能會在不同的記憶體空間中,這點和 Go 語言中的指標很像。下面的例子清晰地說明了這些情況:

type Rect1 struct {Min, Max Point }
type Rect2 struct {Min, Max *Point }

參考:[1] https://www.cnblogs.com/liyutian/p/10050320.html
[2] https://blog.csdn.net/wohu1104/article/details/106202892