golang(3)

八,struct

struct的定義如下: type 結構體名 struct{},其中定義的變量不要var,但是仍然是倒序。
結構體變量的初始化:結構體名{}括號中的變量按照定義的順序依次填寫;如果不想寫全,或者不想按順序寫,還可以寫成json的格式。(這個爲後續添加變量,而無需修改賦值提供了方便);
匿名變量的訪問:在S1中有一個匿名變量S,對於S中的變量的訪問可以直接寫a.name如13行所示;當然也可以寫成a.S.name(其變量明就是S); 如果S1中又定義了name,寫全可以訪問到S中的變量了;
由於*S和S中的變量的訪問方法相同,所以不能同時存在一個S一個*S的匿名變量;

點擊(此處)摺疊或打開

  1. package main

  2. type S struct{
  3.     age int
  4.     name string
  5. }
  6. type S1 struct{
  7.     S
  8. }
  9. func main() {
  10.     var a S1=S1{S{10,"tom"}}
  11.    // var a S1=S1{S{name:"tom"}}
  12.     println(a.name);
  13. }

九 函數&方法

函數的聲明和C也是差不多的。就是變量的類型和返回值都是寫在後面的。還有可以有多個返回值哦。
當函數帶上receiver就變成方法了,如第10行所示;

點擊(此處)摺疊或打開

  1. package main

  2. type iface interface {
  3.     setName()
  4. }
  5. type S1 struct {
  6.     name string
  7. }

  8. func (s *S1) setName() {
  9.     s.name = "hello"
  10. }

  11. func main() {
  12.     var a S1
  13.     (*S1).setName(&a) //a.setName()
  14.     println(a.name)
  15. }
16行有兩種寫法:通過彙編查看,可以發現這兩者指示寫法不同,實際是等價的。都是調用了callq  0x400c00 <main.(*S1).setName>方法;其中a.setName被編譯器自動換成了(&a).setName;
這就a.setName的調用發發有點類似面向對象的寫法了。如果將地10行的*去掉。則調用該函數是會創建一個臨時的S1對象。讓後再對其賦值。等函數退出後,之際調用的便量並沒有得到修改。那麼這個函數還有什麼作用呢? 爲什麼還要提供寫法的?


當然由於slice的特殊性,當slice作爲參數時,其內容被更改了,還是可以反應到實際數組中去的。如下面的代碼,實際打印結果是10;因爲此處傳值指示傳的slice本身,而其指向的數組還是同一個。要想讓b指向另一個數組,就必須得用指針型了。
還有注意點,就是原始類型不能直接作爲receiver;

點擊(此處)摺疊或打開

  1. package main
  2. type A []int
  3. func (s A) setValue() {
  4.     s[0] = 10
  5. }
  6. /*func (s *A) setValue()
  7.     (*s)[0] = 10 //注意,此處必須帶*號,編譯器已經無法自動轉換了。如上例的第11行;
  8. }*/
  9. func main() {
  10.     var a = [...]int{1, 2, 3, 4, 5, 6, 7, 8}
  11.     b := A(a[1:6])
  12.     b.setValue()
  13.     println(a[1])
  14. }

十,interface

上面的例子裏面已經用到了interface;和java相同,interface是一個定義了一系列方法的集合。但是與java不同的是,此處的interface不需要明確標明implents,只要一個類型的被作爲receiver實現了所有的interface的方法,編譯器就能自動識別該類型實現了該interface;
如下面的代碼:S1實現了myface;由代碼的19,20行可以發現,無論是送入S1的指針,還是對象,都能實現對setName的調用,如果setName的receiver是*S;則20行會報錯。這就應證了golang手冊中的一句話,T的方法集包含receiver爲T 和*T的所有方法,而*T的方法集只包含receiver爲*T的方法;(更通俗的表達方法時,當參數(receiver是T)時,調用該方法的對象既可以時T,也可以時*T; 當receiver爲*T時,調用時的參數只能時*T; 所以當interface作爲參數時,具體是傳入T還是*T。需要看該對象的具體實現,如果該對象的receiver只有*T,則參數只能傳入*T,否則可以任選T或者*T; 當然只有當receiver爲*T,且參數傳入*T時,纔可能真正修改到T對象的成員變量;

點擊(此處)摺疊或打開

  1. package main

  2. type myface interface {
  3.     setName()
  4. }
  5. type S1 struct {
  6.     name string
  7. }

  8. func (s S1) setName() {
  9.     s.name = "hello"
  10. }
  11. func test(a myface) {
  12.     a.setName()
  13. }

  14. func main() {
  15.     var a, b S1
  16.     test(&a)
  17.     test(b)
  18.     println(a.name)
  19.     println(b.name)
  20. }
下面關心的是,在test函數中a是個什麼東西?在調用test(&a)時,14行的a爲:{tab = 0xc200035000, data = 0xc20001b010};第一個參數不知道幹嗎的,但是data中的值是19行 info loacals 時,系統顯示的&a的值;
當20行的掉用進入到14行時:a的內容爲: {tab = 0xc200035030, data = 0xc20001b020},data是一個緊依賴於上面的一個空間。

由上面兩個聯合得知在19行運行info locals的輸出。
(gdb) i locals 
b = {name = {str = 0x0, len = 0}}
&a = 0xc20001b010

以及:p &b.name
$11 = (struct string *) 0x7fffe7f73f58
結合上面的結果可以看出此處輸出的&a不是a的地址,而是某個中間狀態得東西,這個和test(b)進入test函數後的內容類似;
經過查看內存發現 當今如14行時,此處的a的大小是固定的只包含兩個指針的變量,所以sizeof(a)=2*sizeof(void*);而*(a.data)是一個內容空間,而該空間實際是test,傳值時的一個拷貝;


下面在看看*T的receiver的結果:

點擊(此處)摺疊或打開

  1. package main

  2. type myface interface {
  3.     setName()
  4. }
  5. type S1 struct {
  6.     name string
  7.     age int
  8. }

  9. func (s* S1) setName() {
  10.     s.age=11
  11. }
  12. func test(a myface) {
  13.     a.setName()
  14. }

  15. func main() {
  16.     var a, b S1 = S1{name:"tom"}, S1{name:"java"}
  17.     test(&a)
  18.     test(&b)
  19.     println(a.name)
  20.     println(b.name)
  21. }
此時15行a.data的值就是&a的地址;所以說interface 作爲參數時,其有兩個field,第二個field,data其中包含的是參數的地址,/*不同的是 在*T的method中,data的值直接位傳入參數的值,而T的method中是指向參數的一個拷貝*/,  不同的是,當傳入的參數時指針時,data直接指向該指針的位置(data的值就是參數的地址),當傳入的參數是對象時,該對象會被複制一份,然後data指向該新複製的對象,即其值是新對象的地址;


點擊(此處)摺疊或打開

  1. package main

  2. type myface interface {//接口,只有一個方法
  3.     setName()
  4. }
  5. type S1 struct { //結構體S1
  6.     name string
  7. }
  8. type S2 struct { //結構體S2
  9.     name string;
  10. }

  11. func (this S2)setName(){ //S2的receiver是T;
  12.     this.name="s2";
  13. }

  14. func (s *S1) setName() { //S1的receiver是*T;
  15.     s.name = "S1"
  16. }

  17. func test1(a myface){
  18.     a.setName(); //調用接口的方法;
  19. }

  20. func main(){
  21.     a2:=S2{name: "aS2"}
  22.     a1:=S1{name:"aS1"};
  23.     //b1:=S1{name: "bS1"}
  24.     b2:=S2{name:"bS2"}
  25.     test1(&a1) //S1的receiver是*T,所以值接受*T參數;
  26.     //test1(b1)
  27.     test1(&a2) //S2的receiver是T,所以接受*T;
  28.     test1(b2) //S2的receiver是T,所以也接受T;
  29.     println("a1.name=",a1.name); //S1,這是因a1的內存地址最終能傳到*S1.setName;
  30.     println("a2.name=",a2.name); //aS2(沒有變),a2的地址雖然能到達test1,但是S2.setName中又複製了一個變量;
  31.     println("b2.name=",b2.name); //bS2(沒有變),b2到達test1時已經複製了一份,但是到s2的setName又複製了,總計複製兩遍。肯定達不到效果了
  32. }
對於interface的結構,今天在官方文檔中找到了說明:A variable of interface type stores a pair: the concrete value assigned to the variable, and that value's type descriptor. To be more precise, the value is the underlying concrete data item that implements the interface and the type describes the full type of that item
詳細參見:http://golang.org/doc/articles/laws_of_reflection.html; 或者其中的blog:http://research.swtch.com/interfaces
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章