2015-10-07 3 views

ответ

6

Короткий ответ: потому что это работает GObject. Длинный ответ - это всего лишь кусочек дольше:

C не имеет объектов или конструкторов, он имеет структуры и функции. Структуры очень просты; они содержат поля, и все. Нет методов, конструкторов, destuctors и т.д. Они выглядят следующим образом:

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

К «Instantiate» на структуру, то выделить необходимую память (либо в стеке или в куче, я сосредоточусь на куче для остальная часть вопроса) и установите поля.

Это быстро становится проблемой даже для нашей простой структуры, поэтому вы обычно видите функции, которые помогут в распределении и освобождении структур. Что-то вроде этого:

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); 
} 

В Vala в *_new функций отображаются именованными конструкторы. Вала связывания для этого может выглядеть примерно так:

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

    public int bar; 
    public string? baz; 
} 

Это все довольно просто, но то, что происходит, когда вы хотите, чтобы «расширить» Foo и добавить новое поле? 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 выделяет кусок sizeof(Foo) байтов на кучу (с использованием malloc) - место для всего поля quux! Это означает, что мы не можем назвать нашу функцию foo_new.

Если вы звоните в библиотеку, написанную в Вала, есть способ обойти это: помимо функции 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 класса в Vala подклассов 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; 
} 

К сожалению, код не написан в Vala редко следует этой конвенции (Grep для '' has_construct_function атрибута CCODE в VAPIs распределенных с valac).

На этом этапе вы можете подумать: «Это боль, но почему бы просто не воссоздать содержимое функции foo_new в qux_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 't вызывается при использовании 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 

 Смежные вопросы

  • Нет связанных вопросов^_^