Go基礎系列:struct和嵌套struct

來源:互聯網
上載者:User

標籤:傳遞值   順序   err   欄位名   程式   父類   oat   執行個體   go基礎   

struct

struct定義結構,結構由欄位(field)組成,每個field都有所屬資料類型,在一個struct中,每個欄位名都必須唯一。

說白了就是拿來儲存資料的,只不過可自訂化的程度很高,用法很靈活,Go中不少功能依賴於結構,就這樣一個角色。

Go中不支援物件導向,物件導向中描述事物的類的重擔由struct來挑,這種模式稱為組合(composite)。物件導向中父類與子類、類與對象的關係是is a的關係,例如Horse is a Animal,Go中的組合則是外部struct與內部struct的關係、struct執行個體與struct的關係,它們是has a的關係。Go中通過struct的composite,可以"模仿"很多物件導向中的行為,它們很"像"。

定義struct

定義struct的格式如下:

type identifier struct {    field1 type1    field2 type2    …}// 或者type T struct { a, b int }

理論上,每個欄位都是有具有唯一性的名字的,但如果確定某個欄位不會被使用,可以將其名稱定義為空白標識符_來丟棄掉:

type T struct {    _ string    a int}

每個欄位都有類型,可以是任意類型,包括內建單一資料型別、其它自訂的struct類型、當前struct類型本身、介面、函數、channel等等。

如果某幾個欄位類型相同,可以縮寫在同一行:

type mytype struct {    a,b int    c string}
構造struct執行個體

定義了struct,就表示定義了一個資料結構,或者說資料類型,也或者說定義了一個類。總而言之,定義了struct,就具備了成員屬性,就可以作為一個抽象的模板,可以根據這個抽象模板產生具體的執行個體,也就是所謂的"對象"。

例如:

type person struct{    name string    age int}// 初始化一個person執行個體var p person

這裡的p就是一個具體的person執行個體,它根據抽象的模板person構造而出,具有具體的屬性name和age的值,雖然初始化時它的各個欄位都是0值。換句話說,p是一個具體的人。

struct初始化時,會做預設的賦0初始化,會給它的每個欄位根據它們的資料類型賦予對應的0值。例如int類型是數值0,string類型是"",參考型別是nil等。

因為p已經是初始化person之後的執行個體了,它已經具備了實實在在存在的屬性(即欄位),所以可以直接存取它的各個屬性。這裡通過訪問屬性的方式p.FIELD為各個欄位進行賦值。

// 為person執行個體的屬性賦值,定義具體的personp.name = "longshuai"p.age = 23

擷取某個屬性的值:

fmt.Println(p.name) // 輸出"longshuai"

也可以直接賦值定義struct的屬性來產生struct的執行個體,它會根據值推斷出p的類型。

var p = person{name:"longshuai",age:23}p := person{name:"longshuai",age:23}// 不給定名稱賦值,必須按欄位順序p := person{"longshuai",23}p := person{age:23}p.name = "longshuai"

如果struct的屬性分行賦值,則必須不能省略每個欄位後面的逗號",",否則就會報錯。這為未來移除、添加屬性都帶來方便:

p := person{    name:"longshuai",    age:23,     // 這個逗號不能省略}

除此之外,還可以使用new()函數或&TYPE{}的方式來構造struct執行個體,它會為struct分配記憶體,為各個欄位做好預設的賦0初始化。它們是等價的,都返回資料對象的指標給變數,實際上&TYPE{}的底層會調用new()。

p := new(person)p := &person{}// 產生對象後,為屬性賦值p.name = "longshuai"p.age = 23

使用&TYPE{}的方式也可以初始化賦值,但new()不行:

p := &person{    name:"longshuai",    age:23,}

選擇new()還是選擇&TYPE{}的方式構造執行個體?完全隨意,它們是等價的。但如果想要初始化時就賦值,可以考慮使用&TYPE{}的方式。

struct的值和指標

下面三種方式都可以構造person struct的執行個體p:

p1 := person{}p2 := &person{}p3 := new(person)

但p1和p2、p3是不一樣的,輸出一下就知道了:

package mainimport (    "fmt")type person struct {    name string    age  int}func main() {    p1 := person{}    p2 := &person{}    p3 := new(person)    fmt.Println(p1)    fmt.Println(p2)    fmt.Println(p3)}

結果:

{ 0}&{ 0}&{ 0}

p1、p2、p3都是person struct的執行個體,但p2和p3是完全等價的,它們都指向執行個體的指標,指標中儲存的是執行個體的地址,所以指標再指向執行個體,p1則是直接指向執行個體。這三個變數與person struct執行個體的指向關係如下:

 變數名      指標     資料對象(執行個體)-------------------------------p1(addr) -------------> { 0}p2 -----> ptr(addr) --> { 0}p3 -----> ptr(addr) --> { 0}

所以p1和ptr(addr)儲存的都是資料對象的地址,p2和p3則儲存ptr(addr)的地址。通常,將指向指標的變數(p1、p2)直接稱為指標,將直接指向資料對象的變數(p1)稱為對象本身,因為指向資料對象的內容就是資料對象的地址,其中ptr(addr)和p1儲存的都是執行個體對象的地址。

儘管一個是資料對象值,一個是指標,它們都是資料對象的執行個體。也就是說,p1.namep2.name都能訪問對應執行個體的屬性。

var p4 *person呢,它是什嗎?該語句表示p4是一個指標,它的指向對象是person類型的,但因為它是一個指標,它將初始化為nil,即表示沒有指向目標。但已經明確表示了,p4所指向的是一個儲存資料對象地址的指標。也就是說,目前為止,p4的指向關係如下:

p4 -> ptr(nil)

既然p4是一個指標,那麼可以將&person{}new(person)賦值給p4。

var p4 *personp4 = &person{    name:"longshuai",    age:23,}fmt.Println(p4) 

上面的代碼將輸出:

&{longshuai 23}
傳值 or 傳指標

Go函數給參數傳遞值的時候是以複製的方式進行的。

複製傳值時,如果函數的參數是一個struct對象,將直接複製整個資料結構的副本傳遞給函數,這有兩個問題:

  • 函數內部無法修改傳遞給函數的未經處理資料結構,它修改的只是未經處理資料結構拷貝後的副本
  • 如果傳遞的未經處理資料結構很大,完整地複製出一個副本開銷並不小

所以,如果條件允許,應當給需要struct執行個體作為參數的函數傳struct的指標。例如:

func add(p *person){...}

既然要傳指標,那struct的指標何來?自然是通過&符號來擷取。分兩種情況,建立成功和尚未建立的執行個體。

對於已經建立成功的struct執行個體p,如果這個執行個體是一個值而非指標(即p->{person_fields}),那麼可以&p來擷取這個已存在的執行個體的指標,然後傳遞給函數,如add(&p)

對於尚未建立的struct執行個體,可以使用&person{}或者new(person)的方式直接產生執行個體的指標p,雖然是指標,但Go能自動解析成執行個體對象。然後將這個指標p傳遞給函數即可。如:

p1 := new(person)p2 := &person{}add(p1)add(p2)
struct field的tag屬性

在struct中,field除了名稱和資料類型,還可以有一個tag屬性。tag屬性用於"注釋"各個欄位,除了reflect包,正常的程式中都無法使用這個tag屬性。

type TagType struct { // tags    field1 bool   "An important answer"    field2 string "The name of the thing"    field3 int    "How much there are"}
匿名欄位和struct嵌套

struct中的欄位可以不用給名稱,這時稱為匿名欄位。匿名欄位的名稱強制和類型相同。例如:

type animal struct {    name string    age int}type Horse struct{    int    animal    sound string}

上面的Horse中有兩個匿名欄位intanimal,它的名稱和類型都是int和animal。等價於:

type Horse struct{    int int    animal animal    sound string}

顯然,上面Horse中嵌套了其它的struct(如animal)。其中animal稱為內部struct,Horse稱為外部struct。

以下是一個嵌套struct的簡單樣本:

package mainimport (    "fmt")type inner struct {    in1 int    in2 int}type outer struct {    ou1 int    ou2 int    int    inner}func main() {    o := new(outer)    o.ou1 = 1    o.ou2 = 2    o.int = 3    o.in1 = 4    o.in2 = 5    fmt.Println(o.ou1)  // 1    fmt.Println(o.ou2)  // 2    fmt.Println(o.int)  // 3    fmt.Println(o.in1)  // 4    fmt.Println(o.in2)  // 5}

上面的o是outer struct的執行個體,但o除了具有自己的顯式欄位ou1和ou2,還具備int欄位和inner欄位,它們都是嵌套欄位。一被嵌套,內部struct的屬性也將被外部struct擷取,所以o.into.in1o.in2都屬於o。也就是說,外部struct has a 內部struct,或者稱為struct has a field

上面的outer執行個體,也可以直接賦值構建:

o := outer{1,2,3,inner{4,5}}

在賦值inner中的in1和in2時不能少了inner{},否則會認為in1、in2是直接屬於outer,而非嵌套屬於outer。

顯然,struct的嵌套類似於物件導向的繼承。只不過繼承的關係模式是"子類 is a 父類",例如"轎車是一種汽車",而嵌套struct的關係模式是外部struct has a 內部struct,正如上面樣本中outer擁有inner

嵌套struct的名稱衝突問題

假如外部struct中的欄位名和內部struct的欄位名相同,會如何?

有以下兩個名稱衝突的規則:

  1. 外部struct覆蓋內部struct的同名欄位、同名方法
  2. 同層級的struct出現同名欄位、方法將報錯

第一個規則使得Go struct能夠實現物件導向中的重寫(override),而且可以重寫欄位、重寫方法。

第二個規則使得同名屬性不會出現歧義。例如:

type A struct {    a int    b int}type B struct {    b float32    c string    d string}type C struct {    A    B    a string    c string}var c C

按照規則(1),直屬於C的a和c會分別覆蓋A.a和B.c。可以直接使用c.a、c.c分別訪問直屬於C中的a、c欄位,使用c.d或c.B.d都訪問屬於嵌套的B.d欄位。如果想要訪問內部struct中被覆蓋的屬性,可以c.A.a的方式訪問。

按照規則(2),A和B在C中是同層級的嵌套結構,所以A.b和B.b是衝突的,將會報錯,因為當調用c.b的時候不知道調用的是c.A.b還是c.B.b。

Go基礎系列:struct和嵌套struct

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.