回答
答案很簡單:因爲這是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_new
。 Calling 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
- 1. 斯卡拉同構類型
- 2. 類型...不是構造函數類型
- 3. javascript:相同類型的對象的不同構造函數
- 4. 如何調用類的構造泛型類型的構造
- 5. 斯卡拉遞歸類型和類型構造函數實現
- 6. 構造泛型類型
- 7. 找不到類型''的構造函數
- 8. 在構造函數中調用不同類的構造函數?
- 9. 從不同類的構造函數調用構造函數
- 10. 類型的對象構造
- 11. C#ITypeResolutionService的構造類型
- 12. 構造不同類型的n元樹C++
- 13. 構造enum類型並轉發不同數量的參數
- 14. UB具有不同類型的結構鑄造?
- 15. 斯卡拉 - 從泛型類型構造方法名稱?
- 16. 不同造型
- 17. 斯卡拉 - 相同,但不同類型
- 18. 「構造專輯類專輯不能適用於不同類型的」
- 19. 類型錯誤:google.charts.Bar是不是構造
- 20. 不能使用類型@TypeChecked和構造
- 21. 類型錯誤 - 不是構造函數
- 22. 類型錯誤:WindowActiveXObject是不是構造
- 23. MEAN:類型錯誤:......是不是構造
- 24. 鑄造結構構件的類型
- 25. 不同泛型類型比類構造函數調用正在用
- 26. 斯卡拉類型的構造函數誤解
- 27. 來自序列的斯卡拉動態元組類型構造
- 28. 構造函數返回我無類型而不是類類型
- 29. 在與超類構造函數不同的子類中定義構造函數?
- 30. 構造一個類型