2015-10-07 35 views
2

爲什麼,什麼三個VALA構造?瓦拉不同類型構造的

  • 類構造
  • 構建
  • 方法與類名

和更具體的,爲什麼第三個結構是永遠不會從Gtk.Builder文件中使用時,它叫什麼名字?

回答

6

答案很簡單:因爲這是GObject的是如何工作的。長的答案僅僅是微幅下挫更長:

C沒有物體或構造,它具有結構和功能。結構非常簡單;他們包含領域,就是這樣。沒有方法,構造函數,destuctors等,他們是這樣的:

typedef struct Foo_ { 
    int bar; 
    char* baz; 
} Foo; 

要「實例化」的結構體,可以分配必要的內存(無論是在堆棧或堆上,我把重點放在堆的問題的其餘部分)並設置字段。

這很快得到是一個痛苦,甚至爲我們簡單的結構,所以你通常看到的功能,以幫助以分配和釋放結構。事情是這樣的:

Foo* foo_new (int bar, char* baz) { 
    Foo* foo = malloc (sizeof (Foo)); 
    /* malloc() can fail. Some libraries would return null, some would 
    just assume it never does. GLib-based software generally just exits 
    with an error, which is what we'll do here. */ 
    if (NULL == foo) { 
    fprintf (stderr, "%s:%d: Unable to allocate room for struct Foo\n", 
      __FILE__, __LINE__); 
    exit (EXIT_FAILURE); 
    } 
    foo->bar = bar; 
    foo->baz = (NULL != baz) ? strdup (baz) : NULL; 
    return foo; 
} 

void foo_free (Foo* foo) { 
    if (NULL == foo) 
    return; 

    if (NULL != foo->baz) 
    free (foo->baz); 
    free (foo); 
} 

在瓦拉,在*_new功能映射到命名的構造函數。造成這種情況的瓦拉結合可能看起來像:

[Compact] 
public class Foo { 
    public Foo(); 

    public int bar; 
    public string? baz; 
} 

這一切都非常簡單,但是當你想「擴展」 Foo,並添加一個新的領域會發生什麼? C沒有任何對「擴展」結構的語言級支持。 C程序員通過在子結構中嵌入基本結構來解決這個問題:

typedef struct Qux_ { 
    struct Foo parent; 
    int quux; 
} Qux; 

這是一個相當不錯的C級解決方案; Qux結構的第一部分是完全一樣的一個Foo結構,所以當我們要使用Qux作爲Foo我們所要做的就是投:創建時

void qux_set_bar_and_qux (Qux* qux, int bar, int quux) { 
    ((Foo*) qux)->bar = bar; 
    qux->quux = quux; 
} 

不幸的是它打破了相當嚴重新實例。請記住,我們的foo_new功能(使用malloc)-there房間所有quux場分配的sizeof(Foo)字節的片上堆!這意味着我們不能撥打我們的foo_new功能。

如果您打算使用Vala編寫的庫,有一種解決方法:除了foo_new函數外,Vala實際上會生成foo_construct函數。因此,考慮到像

[Compact] 
public class Foo { 
    public Foo (int bar, string? baz) { 
    this.bar = bar; 
    this.baz = baz; 
    } 
} 

什麼瓦拉將實際產生的東西有點像這樣:現在

void foo_construct (Foo* foo, int bar, char* baz) { 
    foo->bar = bar; 
    foo->baz = g_strdup (baz); 
} 

Foo* foo_new (int bar, char* baz) { 
    Foo* foo = g_malloc (sizeof (Foo)); 
    foo_construct (foo, bar, baz); 
    return foo; 
} 

,如果我們的Qux類瓦拉子類Foo,也可以撥打我們的Foo命名的構造函數:

[Compact] 
public class Qux : Foo { 
    public Qux (int quux) { 
    base (0, "Hello, world"); 
    this.quux = quux; 
    } 

    public int quux; 
} 

由於生成的代碼實際上不叫foo_new,它調用foo_construct

Qux* qux_new (int quux) { 
    Qux* qux = g_malloc (sizeof (Qux)); 
    foo_construct ((Foo*) qux, 0, "Hello, world"); 
    qux->quux = quux; 
} 

可悲的是,在編寫代碼瓦拉不很少遵循此慣例(grep命令在華劣克分佈VAPIs的 'has_construct_function' CCODE屬性)。

在這一點上你可能會想:「這是一種痛苦,但爲什麼不只是在qux_new中重新創建foo_new函數的內容」。那麼,因爲您可能無法訪問Foo結構的內容。編寫Foo的人可能不希望你搞亂他們的私人領域,因此他們可以在公共頭文件中使Foo爲不完整類型,並將完整定義保留給自己。

現在,讓我們開始談論GObject properties。我將詳細介紹一些細節,但基本上它允許您註冊類型,幷包含一些關於它們的信息,這些信息可在運行時使用。

使用GObject註冊的類可以具有屬性。這些在概念上有些類似於C結構中的字段,但是類型爲加載和存儲回調提供了回調,而不是直接讓代碼存儲到地址。這也意味着它可以做,當你設置一個值,以及其他一些方便的東西時會發出一個信號。

GObject中的類初始化相當複雜。我們稍後再討論這個問題,但首先從想要實例化GObject類的庫的角度來看待它。這將是這個樣子:

Qux* qux = g_object_new (QUX_TYPE, 
         "bar", 1729, 
         "baz", "Hello, world", 
         "quux", 1701, 
         NULL); 

這也可能是很明顯的這是什麼一樣:它會創建一個Qux實例,並設置「欄中的」屬性設置爲1729,「巴茲」到「你好,世界」和「 quux「改爲1701.現在回到如何該類被實例化。再次,這是(超過)一點點簡單的,但是......

首先,足夠的內存來保存Qux實例(包括Foo父類,現在還包括GObject類,這是所有GObjects的祖先)被分配。

接下來,調用設置「bar」,「baz」和「qux」屬性的回調。

接下來,調用*_constructor函數。在Vala中,這被映射到construct塊。它看起來是這樣的:

static GObject * foo_constructor (GType type, 
    guint n_construct_properties, 
    GObjectConstructParam construct_properties[n_construct_properties]) { 
    GObjectClass * parent_class = G_OBJECT_CLASS (foo_parent_class); 
    GObject * obj = parent_class->constructor (type, n_construct_properties, construct_properties); 
    Foo * self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_FOO, Foo); 

    /* The code from your construct block goes here */ 

    return obj; 
} 

注意,你沒有得到的參數控制這個功能。如你所見,每個構造函數調用其父類的構造函數。我在你的構造塊的代碼中添加了一個註釋;我們將回到爲什麼它很快與命名構造函數分開。

現在,讓我們看看命名構造函數的代碼。請記住,絕大多數的庫沒有*_construct功能,所以我們可以想象一種不(我們Qux類):

Qux* qux_new (int bar, int quux) { 
    Qux* qux = g_object_new (QUX_TYPE, 
          "bar", bar, 
          "quux", quux, 
          NULL); 
    /* Code from your named constructor goes here. */ 
} 

最後,我們得到的,爲什麼你叫構造ISN在使用GtkBuilder時不叫:它不叫qux_new,它叫g_object_newCalling qux_new is an enormous pain不知道你的圖書館,顯然這是不可能的GtkBuilder知道你的圖書館。

最後,讓我們談談類構造塊。他們基本上是完全不同的東西。幸運的是,解釋它們並不需要太長的時間:當類型的實例被實例化時,在類型被註冊到GObject時被調用,而不是。基本上,第一次你的類被實例化並且不會再被調用。一個簡單的例子:

public class Foo : GLib.Object { 
    class construct { 
    GLib.message ("Hello, world!"); 
    } 

    construct { 
    GLib.message ("%d", this.bar); 
    } 

    public int bar { get; set; default = 1729; } 
} 

private static int main (string[] args) { 
    var foo = new Foo(); 
    foo = new Foo(); 

    return 0; 
} 

將輸出

Hello, world! 
1729 
1729