Объектные методы
Last updated
Was this helpful?
Last updated
Was this helpful?
Так же как в C++, есть много разных способов определить методы объекта и дополнить их: следующее описание и разделы используют словарь C++. (Ожидается что читатель знает основные базовые термины C++. Те кто давно не писал код на C++ могут обратиться например к чтобы освежить свою память.)
невиртуальные общие методы (non-virtual public methods),
виртуальные общие методы (virtual public methods) и
виртуальные закрытые методы (virtual private methods)
Они являются самыми простыми: вам нужно обеспечить простой метод который может взаимодействовать с вашим объектом. Всё что вам нужно сделать это обеспечить прототип функции в заголовочном файле и реализовать её прототип в исходном файле.
В этом нет ничего страшного.
Это предпочтительный способ создания полиморфных объектов GObjects. Всё что вам нужно сделать это определить основной метод и его классовую функцию в общем заголовке, реализовать основной метод в исходном файле и перереализовать классовую функцию в каждом объекте который вас интересует.
Код выше просто переадресует вызов do_action в подходящую классовую функцию. Некоторые пользователи, заинтересованные в производительности, не обеспечивают maman_bar_do_action
функцию оболочку и требуют чтобы пользователи самостоятельно разыменовывали классовый указатель. Это не очень хорошая идея в период инкапсуляции и мешает изменять реализацию объекта в последствие.
Другие пользователи, также заинтересованные в производительности, декларируют maman_bar_do_action
функцию inline в заголовочном файле. Это также мешает изменять реализацию объекта в последствие (хотя проще чем требование от пользователей непосредственного разыменования классовой функции) и часто сложно написать переносимым способом (ключевое слово inline не является частью стандарта C).
Сомневайтесь, пока пользователь не показал вам твёрдые цифры о производительности функционального вызова, по сравнению с простой maman_bar_do_action
в исходном файле.
Пожалуйста, помните что это возможно обеспечит для вас реализованная по умолчанию объектная функция для этого метода класса: инициализируйте поле klass->do_action указателем на фактическую реализацию. Вы можете так же сделать этот метод класса чисто виртуальным инициализировав поле klass->do_action в значение NULL:
Это очень похожие на Virtual Public методы. Просто они не имеют общих функций для непосредственного вызова функции. Заголовочный файл содержит только декларацию классовой функции:
Эти классовые функции часто используются для делегирования части работы в дочерние классы:
Снова, возможно обеспечить реализацию по умолчанию для этой закрытой виртуальной классовой функции:
Наследники могут реализовать подкласс с помощью похожего кода:
Формирование цепочки часто неточно определяется следующим набором условий:
Родительский класс A определяет общий виртуальный метод с именем foo
и обеспечивает реализацию по умолчанию.
Дочерний класс B перереализует метод foo
.
В методе B::foo, дочерний класс B вызывает его родительский классовый метод A::foo.
Эту идиому можно использовать по разному:
Вам нужно изменить поведение класса без изменения его кода. Вы создаёте подкласс чтобы унаследовать его реализацию, перереализуете общий виртуальный метод для незначительного изменения поведения и формируете цепочку убедившись что предыдущее поведение действительно не изменилось, а только расширилось.
Вы ленивы, у вас есть доступ к исходному коду родительского класса но вы не хотите изменять его добавляя вызовы метода в новые специализированные вызовы метода: быстрее хакнуть дочерний класс сформировав цепочку чем изменять родительский для вызова вниз.
Вам нужно реализовать шаблон Chain Of Responsability: каждый объект наследует дерево сформированных цепочек родителя (обычно, в начале или в конце метода) гарантируя что каждый их обработчик выполнится в свою очередь.
Я лично не уверен что два последних способа использования являются хорошей идеей, н так как эта идиома программирования часто используется, этот раздел попытается объяснить как это реализуется.
Для явного формирования цепочки в реализации виртуального метода в родительском классе, вам сначала нужно обработать оригинальную структуру родительского класса. Этот указатель можно зтем использовать для доступа к оригинальному указателю функции класса и вызывать её непосредственно. []
Функция используется для доступа к оригинальной родительской классовой структуре. Её ввод - это указатель на класс производного объекта и она возвращает указатель на оригинальную родительскую структуру класса. Код ниже показывает как вы должны её использовать:
Многие люди использующие эту идиому в GTK+ хранят указатель на родительскую классовую структуру в глобальной статической переменной, чтобы избегать расточительных вызовов для каждого вызова функции. Обычно, class_init callback-функция инициализирует глобальную статичную переменную. Это делает gtk/gtkhscale.c
.
[] Оригинальное прилагательное используемое в этом предложении весьма вредное. Для полного понимания что это значит, вы должны повторить как сструктуры класса инициализируются: для каждого объектного типа, классовая структура привязанная к объекту создаётся с помощью начального копирования классовой сструктуры родительского типа (просто memcpy
) а зтем вызывается class_init callback-функция в результирующей структуре. Так как class_init callback-функция отвечает за перезапись классовой сструктуры с пользовательской перереализацией методов класса, мы не можем просто использовать изменённую копию родительской сструктуры класса сохранённую в нашем производном экземпляре. Мы хотим получить копию классовой сструктуры экземпляра родительского класса.