People often ask why GTK+ was written in C rather than an
object-oriented language. The answer is that C is more
portable and standardly available than any other language.
However, although C lacks syntactic sugar for
object-oriented programming, it in no way prohibits an
object-oriented approach.
GTK+ implements its own custom object system, which offers
standard object-oriented features such as inheritance and
virtual functions. In the tradition of languages such as
Lisp, Smalltalk, and Java, the GTK+ object system is more
runtime-centric than that of C++, allowing interpreted
language bindings and GUI builders to interact with it in
powerful ways.
You may recall from the chapter
called GTK+ Basics that widgets are a special
type of GtkObject; any
object with GtkWidget in its
ancestry is a widget. Widgets represent a region on the
screen---most of them are user interface elements, such as
buttons or menus. There's nothing GUI-specific about GtkObject; the object system can
be used in non-graphical programs.
This chapter dives right into the details of GTK+'s object
system, giving you an idea what's happening "behind the
scenes" in any GTK+ program. Sooner or later you'll need
this information: to write your own objects, debug existing
objects, or just understand GTK+ code on a conceptual
level.
Each GtkObject has two
essential components: a struct representing an instance of the object, and a struct
representing the class. In
general, the instance struct contains the data members
for each instance, and the class struct contains class
function pointers (which can be overridden by
subclasses). The class struct can also contain class data
members---however, it's more typical to use static
variables in the .c file
implementing the object. If you're familiar with C++, the
class struct is equivalent to a vtable, only the class
struct is written by hand. It stores virtual functions
for an object type.
Here are the structs used in
GtkButton:
typedef struct _GtkButton GtkButton;
typedef struct _GtkButtonClass GtkButtonClass;
struct _GtkButton
{
GtkBin bin;
GtkWidget *child;
guint in_button : 1;
guint button_down : 1;
guint relief : 2;
};
struct _GtkButtonClass
{
GtkBinClass parent_class;
void (* pressed) (GtkButton *button);
void (* released) (GtkButton *button);
void (* clicked) (GtkButton *button);
void (* enter) (GtkButton *button);
void (* leave) (GtkButton *button);
};
|
Notice that the first member of
struct _GtkButton is
GtkBin---that's because
GtkButton is a subclass of
GtkBin. (GtkBin is a GtkContainer that can hold one
child.) Since GtkBin is the
first member, we can safely cast a
GtkButton to GtkBin. In
struct _GtkButtonClass,
the same principle applies, and
GtkBinClass is the first member.