Go語言與C/C++類似,C++可通過typedef關鍵字自定義數據類型(別名、定義結構體等),Go語言則通過type關鍵字可實現自定義類型的實現
1、自定義類型格式
用戶自定義類型使用type,其語法格式為:
type newType oldType
oldType可以是自定義類型、預聲明類型、未命名類型中的任意一種
newType是新類型標識符,與oldType具有相同的底層類型,並且都繼承了底層類型的操作集合(如底層類型是map,支持range迭代訪問,則新類型也可以使用range迭代訪問)。此外,newType和oldType是兩個完全不同的類型,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



