這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Go 有指標。然而卻沒有指標運算,因此它們更象是引用而不是你所知道的來自於 C 的指標。指標非常有用。在 Go 中調用函數的時候,得記得變數是值傳遞的。因此,為了修改一個傳遞入函數的值的效率和可能性,有了指標。
var p *intfmt.Printf("%v", p) // 列印nilvar i int // 定義一個整型變數ip = &i // 使得p指向ifmt.Printf("%v", p) // 列印出來的內容類別似0x7ff96b81c000a
記憶體配置
用new分配記憶體
內建函數 new 本質上說跟其他語言中的同名函數功能一樣:new(T) 分配了零值填充 的 T 類型的記憶體空間,並且返回其地址,一個 *T 類型的值。用 Go 的術語說,它返回 了一個指標,指向新分配的類型 T 的零值。記住這點非常重要。
用make分配記憶體
內建函數 make(T, args) 與 new(T) 有著不同的功能。它只能建立 slice,map 和 channel,並且返回一個有初始值(非零)的 T 類型,而不是 *T。本質 來講,導致這三個類型有所不同的原因是指向資料結構的引用在使用前必須被初始化。 例如,一個 slice,是一個包含指向資料(內部 array)的指標,長度和容量的三項描述 符;在這些項目被初始化之前,slice 為 nil。對於 slice,map 和 channel,make 初始 化了內部的資料結構,填充適當的值。
new分配;make初始化
- new(T)返回*T指向一個零值T
- make(T)返回初始化後的T
make 僅適用於 map,slice 和 channel,並且返回的不是指標。應當用 new 獲 得特定的指標。
建構函式與符合聲明
func NewFile(fd int, name string) *File { if fd<0 { return nil } f := File{fd, name, nil, 0} // 建立一個新的File return &f // 返回 f 的地址 }
返回本地變數的地址沒有問題;在函數返回後,相關的儲存地區仍然存在。 事實上,從複合聲明擷取分配的執行個體的地址更好(從複合聲明中擷取地址,意味著告訴編譯器在堆中分配空間,而不是棧中),因此可以最終將兩行縮短到一行。
return &File{fd, name, nil, 0}
所有的項 目(稱作 欄位)都必須按順序全部寫上。然而,通過對元素用欄位: 值成對的標識,初 始化內容可以按任意順序出現,並且可以省略初始化為零值的欄位。因此可以這樣
return &File{fd: fd, name: name}
在特定的情況下,如果複合聲明不包含任何欄位,它建立特定類型的零值。運算式new(File)
和 &File{}
是等價的。
複合聲明同樣可以用於建立 array,slice 和 map,通過指定適當的索引和 map 鍵來標 識欄位。在這個例子中,無論是 Enone,Eio 還是 Einval 初始化都能很好的工作,只 要確保它們不同就好了。
ar := [...] s t r i n g { Enone: "no error", Einval: "invalid argument" } sl := [] s t r i n g { Enone: "no error", Einval: "invalid argument" }ma := map[int]string {Enone: "no error", Einval: "invalid argument"}
定義自己的類型
Go允許定義新的類型,通過關鍵字type實現:
例如:
type foo int \\ 建立了一個新的類型 foo 作用跟 int 一樣\\ 建立更加複雜的類型需要用到 struct 關鍵 字。package mainimport "fmt"type NameAge struct { name string // 首字母小寫,不匯出 age int // 不匯出}func main() { a := new(NameAge) a.name = "Pete"; a.age = 42 fmt.Printf("%v\n", a) // 輸出&{Pete 42}}
方法
給新定義的類型建立函數有兩個途徑:
// 1、建立一個函數接受這個類型的參數。func doSomething(n1 *NameAge, n2 int) { /* */ }// 2、建立一個接收方是該類型的方法func (n1 *NameAge) doSomething(n2 int) { /* */ }//方法調用方式var n *NameAgen.doSomething(2)
// Mutex 資料類型有兩個方法,Lock 和 Unlock。type Mutex struct { /* Mutex 欄位 */ } func (m *Mutex) Lock(){ /* Lock 實現 */ } func (m *Mutex) Unlock(){ /* Unlock 實現 */ }//現在用兩種不同的風格建立了兩個資料類型。type NewMutex Mutextype PrintableMutex struct {Mutex}//現在 NewMutux 等同於 Mutex,但是它沒有任何 Mutex 的方法。換句話說,它的方法 是空的。但是 PrintableMutex 已經從 Mutex 繼承了方法集合。*PrintableMutex 的方法集合包含了 Lock 和 Unlock 方法,被綁定到其匿名欄位 Mutex。
轉換
Go支援將一個類型轉換為另一個類型
// 1、string->byte或rune的slicemystring := "hello this is string"byteslice := []byte(mystring) // 轉換成byte slice,每個byte儲存字串對應位元組的整數值。注意Go的字串是UTF-8編碼,位元組長度不定。runeslice := []rune(mystring) // 轉換到rune slice,每個rune儲存Unicode編碼的指標。字串中的每個字元對應一個整數。// 2、從位元組或整型的slice到stringb := []byte {'h','e','l','l','o'} // 複合聲明 s := string(b)i := []rune {257,1024,65}r := string(i)// 3、數值的轉換• 將整數轉換到指定的(bit)長度:uint8(int);• 從浮點數到整數:int(float32)。這會截斷浮點數的小數部分; • 其他的類似:float32(int)。// 4、使用者定義型別的轉換type foo struct { int } // 匿名欄位 type bar foo // bar是foo的別名var b bar = bar{1} // 聲明b為bar類型var f foo = b //,不能使用b(類型bar)作為類型foo賦值var f foo = foo(b)//,可以通過此方式轉化
組合
Go 不是物件導向語言,因此並沒有繼承。但是有時又會 需要從已經實現的類型中“繼承”並修改一些方法。在 Go 中可以用嵌入一個類型的方 式來實現。