Инстанциируемые классифицируемые типы: объекты

Типы которые регистрируются с классом и объявляются инстанциированными очень похожи на объекты object. Хотя GObjects (детали в The GObject base class) самый известный тип инстанциированного классифицированного типа, другие виды подобных объектов используемых как основа иерархии наследования была внешней разработкой и они все основаны на базовых особенностях описанных ниже.

Например, код показанный ниже демонстрирует как вы должны регистрировать такие базовые типовые объекты в системе типов:

typedef struct { GObject parent; /* instance members */ int field_a; } MamanBar; typedef struct { GObjectClass parent; /* class members */ void (*do_action_public_virtual) (MamanBar *self, guint8 i); void (*do_action_public_pure_virtual) (MamanBar *self, guint8 i); } MamanBarClass; #define MAMAN_BAR_TYPE (maman_bar_get_type ()) GType maman_bar_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (MamanBarClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) foo_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (MamanBar), 0, /* n_preallocs */ (GInstanceInitFunc) NULL /* instance_init */ }; type = g_type_register_static (G_TYPE_OBJECT, "BarType", &info, 0); } return type; }

После первого вызова maman_bar_get_type, тип с именем BarType будет зарегистрирован в системе типов как наследник типа G_TYPE_OBJECT.

Каждый объект должен определяться двумя структурами: структурой класса и структурой объекта. Все сструктуры класса должны содержать GTypeClass в качестве первого члена. Все сструктуры объекта должны содержать структуру GTypeInstance в качестве первого члена. Декларация этих C типов, взята из gtype.h, показана ниже:

struct _GTypeClass { GType g_type; }; struct _GTypeInstance { GTypeClass *g_class; };

Эти ограничения позволяют системе типов удостовериться в том, что каждый экземпляр объекта (идентифицируемый указателем на экземпляр объектной сструктуры) содержит в первом байте указатель на структура класса объекта.

Эти взаимоотношения лучше демонстрируются примером: давайте возьмём объект B который наследуется из объекта A:

/* A definitions */ typedef struct { GTypeInstance parent; int field_a; int field_b; } A; typedef struct { GTypeClass parent_class; void (*method_a) (void); void (*method_b) (void); } AClass; /* B definitions. */ typedef struct { A parent; int field_c; int field_d; } B; typedef struct { AClass parent_class; void (*method_c) (void); void (*method_d) (void); } BClass;

C стандартом принято что первое поле C сструктуры начинается в первом байте буфера используемого для содержания структурных полей в памяти. Это значит что первое поле экземпляра объекта B - это первое поле A's которое в свою очередь является первым полем GTypeInstance's которая в свою очередь является g_class, указанным в структуре класса B's.

Благодаря этим условиям, возможно определить тип каждого объекта выполнив:

B *b; b->parent.parent.g_class->g_type

или, намного быстрее:

B *b; ((GTypeInstance*)b)->g_class->g_type

Инициализация и Уничтожение

Инстанциирование этих типов может быть выполнено с помощью g_type_create_instance:

GTypeInstance* g_type_create_instance (GType type); void g_type_free_instance (GTypeInstance *instance);

g_type_create_instance ищет информационную структуру типа связанную с запрошенным типом. Затем, размер экземпляра и инстанциируемую политику (если поле n_preallocs установлено не в нулевое значение, система типов распределяет экземпляр сструктуры объекта в участок памяти вместо распределения каждого экземпляра) объявленную пользователем используемую для получения буфера содержащего экземпляр объектной сструктуры.

Если это первое инстанциирование создаваемого объекта, система типов должна создать структуру класса: это распределит буфер содержащий структуру объектного класса и инициализирует его. Сначала копируется родительская структура класса поверх этой сструктуры (если нет родителя, инициализируется нулём). Затем вызываются функции base_class_initialization (GBaseInitFunc) от самого высшего базового объекта до самого низшего объекта. Объектная функция class_init (GClassInitFunc) вызывается впоследствии, завершая инициализацию сструктуры класса. Наконец, инициализируется интерфейс объекта (позже мы обсудим инициализацию интерфейса более детально). [4]

Как только система типов получает указатель на инициализированную структуру класса, она устанавливает указатель экземпляра объектного класса в структуру объектного класса и вызывает объектные функции instance_init (GInstanceInitFunc), из самого высшего базового типа до самого низшего типа.

Уничтожается объект через g_type_free_instance очень просто: экземпляр сструктуры возвращается в пул если существует, и если это был последний уничтоженный экземпляр объекта, класс уничтожается.

Уничтожение класса [5] (понятие уничтожение иногда описывается как завершение (finalization) в GType) это симметричный процесс инициализации: интерфейсы уничтожаются первыми. Затем, вызывается функция class_finalize (ClassFinalizeFunc). Наконец вызываются функции base_class_finalize (GBaseFinalizeFunc) от самого низшего типа до самого высшего базового типа и структура класса освобождается.

Как многие читатели поняли, основы процесса инициализации/завершения очень похожи на парадигму C++ Constructor/Destructor. Практически различия в деталях, хотя это не важно при внешнем сходстве. Обычно, большинству пользователей достаточно знать что C++ constructor (то есть, список методов объекта вызывается в экземпляре объекта один раз для каждого типа иерархии наследования) не существует в GType и должны основываться на предлагаемых особенностях GType. Аналогично, GTypes не имеет механизма деструкции. Ответственность за правильную семантику уничтожения существующего кода GType, лежит на пользователе. (это то что делает GObject. Смотрите The GObject base class)

Например, если объект B который происходит из A инстанциирован, GType только вызовет instance_init объекта B, в то время как C++ окружение вызовет конструктор объекта A первым, а зтем типового объекта B. Кроме того, C++ код эквивалентный base_init и class_init callback-функциям GType обычно не необходим, потому что C++ не может создавать типовые объекты во время выполнения.

Процессс инстанциирования/завершения можно резюмировать следующим:

Таблица 1. GType Instantiation/Finalization

Время вызова Вызываемая функция Параметры функции
Первый вызов g_type_create_instance для целевого типа типовая функция base_init В дереве наследования класса из базового типа для целевого типа. base_init вызывается один раз для каждой сструктуры класса.
функция целевого типа class_init В структуре класса целевого типа
инициализация интерфейса, смотрите раздел с названием “Interface Initialization”  
Каждый вызов g_type_create_instance для целевого типа функция instance_init целевого типа В экземпляре объекта
Последний вызов g_type_free_instance для целевого типа уничтожение интерфейса, смотрите раздел с названием “Interface Destruction”  
функция class_finalize целевого типа В структуре класса целевого типа
типовая функция base_finalize В дереве наследования классов из базового типа в целевой тип. base_finalize вызывается один раз для каждой сструктуры класса.




[4] Процессс инициализации класса полностью реализован type_class_init_Wm в gtype.c.

[5] Это реализовано type_data_finalize_class_Ugtype.c).