![]() |
![]() |
![]() |
![]() |
Just as with C++, there are many different ways to define object methods and extend them: the following list and sections draw on C++ vocabulary. (Readers are expected to know basic C++ concepts. Those who have not had to write C++ code recently can refer to e.g. http://www.cplusplus.com/doc/tutorial/ to refresh their memories.)
non-virtual public methods,
virtual public methods and
virtual private methods
These are the simplest, providing a simple method which acts on the object. Provide a function prototype in the header and an implementation of that prototype in the source file.
1 2 3 4 5 6 7 8 9 10 11 |
/* declaration in the header. */ void maman_bar_do_action (MamanBar *self, /* parameters */); /* implementation in the source file */ void maman_bar_do_action (MamanBar *self, /* parameters */) { g_return_if_fail (MAMAN_IS_BAR (self)); /* do stuff here. */ } |
This is the preferred way to create GObjects with overridable methods:
Define the common method and its virtual function in the class structure in the public header
Define the common method in the header file and implement it in the source file
Implement a base version of the virtual function in the source
file and initialize the virtual function pointer to this
implementation in the object’s class_init
function; or leave it as NULL
for a ‘pure
virtual’ method which must be overridden by derived classes
Re-implement the virtual function in each derived class which needs to override it
Note that virtual functions can only be defined if the class is
derivable, declared using
G_DECLARE_DERIVABLE_TYPE
so the class structure can be defined.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/* declaration in maman-bar.h. */ #define MAMAN_TYPE_BAR maman_bar_get_type () G_DECLARE_DERIVABLE_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject) struct _MamanBarClass { GObjectClass parent_class; /* stuff */ void (*do_action) (MamanBar *self, /* parameters */); /* Padding to allow adding up to 12 new virtual functions without * breaking ABI. */ gpointer padding[12]; }; void maman_bar_do_action (MamanBar *self, /* parameters */); /* implementation in maman-bar.c */ void maman_bar_do_action (MamanBar *self, /* parameters */) { MamanBarClass *klass; g_return_if_fail (MAMAN_IS_BAR (self)); klass = MAMAN_BAR_GET_CLASS (self); g_return_if_fail (klass->do_action != NULL); klass->do_action (self, /* parameters */); } |
The code above simply redirects the do_action
call
to the relevant virtual function.
It is possible to provide a default
implementation for this class method in the object's
class_init
function: initialize the
klass->do_action
field to a pointer to the
actual implementation.
By default, class methods that are not inherited are initialized to
NULL
, and thus are to be considered "pure virtual".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
static void maman_bar_real_do_action_two (MamanBar *self, /* parameters */) { /* Default implementation for the virtual method. */ } static void maman_bar_class_init (MamanBarClass *klass) { /* this is not necessary, except for demonstration purposes. * * pure virtual method: mandates implementation in children. */ klass->do_action_one = NULL; /* merely virtual method. */ klass->do_action_two = maman_bar_real_do_action_two; } void maman_bar_do_action_one (MamanBar *self, /* parameters */) { MamanBarClass *klass; g_return_if_fail (MAMAN_IS_BAR (self)); klass = MAMAN_BAR_GET_CLASS (self); /* if the method is purely virtual, then it is a good idea to * check that it has been overridden before calling it, and, * depending on the intent of the class, either ignore it silently * or warn the user. */ g_return_if_fail (klass->do_action != NULL); klass->do_action_one (self, /* parameters */); } void maman_bar_do_action_two (MamanBar *self, /* parameters */) { MamanBarClass *klass; g_return_if_fail (MAMAN_IS_BAR (self)); klass = MAMAN_BAR_GET_CLASS (self); if (klass->do_action_two != NULL) klass->do_action_two (self, /* parameters */); } |
These are very similar to virtual public methods. They just don't have a public function to call directly. The header file contains only a declaration of the virtual function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* declaration in maman-bar.h. */ struct _MamanBarClass { GObjectClass parent; /* stuff */ void (* helper_do_specific_action) (MamanBar *self, /* parameters */); /* Padding to allow adding up to 12 new virtual functions without * breaking ABI. */ gpointer padding[12]; }; void maman_bar_do_any_action (MamanBar *self, /* parameters */); |
These virtual functions are often used to delegate part of the job to child classes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* this accessor function is static: it is not exported outside of this file. */ static void maman_bar_do_specific_action (MamanBar *self, /* parameters */) { MAMAN_BAR_GET_CLASS (self)->do_specific_action (self, /* parameters */); } void maman_bar_do_any_action (MamanBar *self, /* parameters */) { g_return_if_fail (MAMAN_IS_BAR (self)); /* random code here */ /* * Try to execute the requested action. Maybe the requested action * cannot be implemented here. So, we delegate its implementation * to the child class: */ maman_bar_do_specific_action (self, /* parameters */); /* other random code here */ } |
Again, it is possible to provide a default implementation for this private virtual function:
1 2 3 4 5 6 7 8 9 |
static void maman_bar_class_init (MamanBarClass *klass) { /* pure virtual method: mandates implementation in children. */ klass->do_specific_action_one = NULL; /* merely virtual method. */ klass->do_specific_action_two = maman_bar_real_do_specific_action_two; } |
Children can then implement the subclass with code such as:
1 2 3 4 5 6 7 8 |
static void maman_bar_subtype_class_init (MamanBarSubTypeClass *klass) { MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass); /* implement pure virtual function. */ bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one; } |