GObject и наследование

Я делал серьезное программное обеспечение на Glib. И я понял, что есть некоторые темы, которые я не очень понимаю. IRC тоже не помогло...

Когда мы занимаемся наследованием, у нас может быть два предложения. Первый A наследует непосредственно от GObject, B наследует от A. Затем я пришел к этому:

https://developer.gnome.org/gobject/stable/chapter-gobject.html

static void
viewer_file_constructed (GObject *obj)
{
  /* update the object state depending on constructor properties */

  /* Always chain up to the parent constructed function to complete object
   * initialisation. */
  G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj);
}

static void
viewer_file_class_init (ViewerFileClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->constructed = viewer_file_constructed;
}

Но когда у вас есть такая договоренность. Дочерний класс делает это: object_class-> built = viewer_file_constructed; в B перекрывает фактически единственный адрес памяти для созданного. Таким образом, это означает, что G_OBJECT_CLASS (viewer_file_parent_class)-> built (obj); будем называть B-> построенный рекурсивно. И это не то, что мы хотим.

Может быть, я не понимаю, но я предполагаю, что структура памяти в B такова:

struct _B
{
  A parent_instance;

  /* instance members */
};

Внутреннее представление должно быть примерно таким:

[   Gobject struct memory ]
[                         ]
[   Gobject variables     ]
[   A struct memory       ]
[   A variables           ] 
[                         ]
[   B struct memory       ]
[   B variables           ] 

Таким образом, память GObject при приведении в B является общей для классов B и A. И все адреса одинаковы на одном и том же я...

  • G_OBJECT_CLASS (B) -> построен
  • G_OBJECT_CLASS (A)-> построен

Это правильно? Так что, если я хочу перезаписать созданный... Должен ли я сохранить указатель, который был раньше, а затем перезаписать его в init с моим? Таким образом, я могу назвать оригинал после того, как я сделаю свою обработку?

То же самое относится и к свойствам. Поскольку A определяет свои свойства с помощью перечисления, оно может быть от 0 до N.

Поэтому я предполагаю, что свойства B должны начинаться с N, а не с 0. В противном случае свойства A обрабатываются B и, возможно, с другими структурами данных и именами.

Проверьте это: https://developer.gnome.org/gobject/stable/gobject-properties.html

enum
{
  PROP_FILENAME = 1,
  PROP_ZOOM_LEVEL,
  N_PROPERTIES
};

Если оба условия определяют свойство с индексом 1. Там будет проблема, потому что glib не знает, кто должен обрабатывать. Я предполагаю, что дочерний класс B будет обрабатывать это, но неправильно, потому что, возможно, B ожидает, скажем, PROP_DIRECTORY, но так как индекс тот же. Смог бы glib отправить в нужное место?

Я могу только сказать, что это будет работать, если в регистр glib добавит некоторое смещение в зависимости от уровня иерархии. Может кто-нибудь объяснить, как это действительно работает? Я не могу найти какой-либо документ с желаемой технической детализацией.

1 ответ

Внутреннее представление должно быть примерно таким:

Не совсем. Там есть разные между GObject а также GObjectClass Структуры. Там есть один GObject экземпляр структуры на экземпляр объекта, но только один GObjectClass экземпляр для всего класса.

Если у вас есть класс FooBar вытекающий из GObject, FooBarClass структура будет выглядеть примерно так:

typedef struct
{
  GObjectClass parent_class;

  /* Virtual methods for FooBar instances: */
  void (*vfunc) (FooBar *self);
} FooBarClass;

Там будет экземпляр FooBarClass в кучу. Поскольку он содержит весь GObjectClass структура как его parent_class член, это означает, что у него есть свой finalize, dispose, get_property и т. д. указатели виртуальных методов.

Отдельно в куче есть экземпляр GObjectClass для типа GObject. Он содержит еще один набор finalize, dispose и т. д. указатели виртуальных методов.

поскольку FooBar происходит от GObject, foo_bar_parent_class будет установлен, чтобы указать на GObjectClass пример. Это то, что позволяет связать.

Так что если вы хотите реализовать constructed виртуальный метод и цепочка (вы должны цепочка constructed), просто сделайте, как пример кода в документации, на которую вы ссылаетесь. Это правильно.


То же самое относится и к свойствам. Поскольку A определяет свои свойства с помощью перечисления, оно может быть от 0 до N.

Неправильно. Когда свойство зарегистрировано с помощью класса g_object_class_install_properties(), индексы свойств связаны с GObjectClass экземпляр внутри структуры класса для этого класса. Они не связаны со стойкой GObjectClass структура для типа GObject. Это тот же принцип, что и выше.

Другими словами, нет глобального реестра индексов свойств: все делается для каждого класса. Таким образом, вы можете (и должны) начать индексирование свойств с 1 для каждого из ваших классов. Они не будут конфликтовать.

Обратите внимание, что, как указано в документации для g_object_class_install_properties(), индекс свойства 0 является особенным и не должен использоваться. Вы должны начать индексирование вашей собственности с 1.


Как говорит ptomato, это далеко за пределы уровня детализации, соответствующего документации. Вы должны прочитать исходный код.

Другие вопросы по тегам