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, это далеко за пределы уровня детализации, соответствующего документации. Вы должны прочитать исходный код.