Up: Readme.md, Prev: Section 6, Next: Section 8
It is more common to make a non-abstract derivable type than abstract type. This section covers how to make non-abstract derivable type objects. A derivable type example is an object for string. It is TStr. And its child is an object for numeric string. A numeric string is a string that expresses a number. For example, "0", "-100" and "123.45". The child object (numeric string) will be explained in the next section.
This section describes memory management for strings before derivable objects.
TStr has a string type value. It is similar to TInt or TDouble but string is more complex than int and double. When you make TStr program, you need to be careful about memory management, which is not necessary to TInt and TDouble.
String is an array of characters that is terminated with '\0'. String is not a C type such as char, int, float or double. But the pointer to a character array behaves like a string type of other languages. So, we often call the pointer string.
If the pointer is NULL, it points nothing. So, the pointer is not a string. Programs with string will include bugs if you aren't careful about NULL pointer.
Another annoying problem is memory allocation. Because string is an array of characters, memory allocation is necessary to create a new string. We don't forget to allocate memory, but often forget to free the memory. It causes memory leak.
char *s;
s = g_strdup ("Hello.");
... ... ... do something with s
g_free (s);g_strdup duplicates a string.
It does:
- Calculates the size of the string. The size of "Hello." is 7 because strings are zero-terminated. The string is an array 'H', 'e', 'l', 'l', 'o', '.' and zero ('\0').
- Requests the system to allocate 7 bytes memories.
- Copies the string to the memory.
- Returns the pointer to the newly-allocated memory.
- If the argument is NULL, then no memory is allocated and it returns NULL.
If the string s is no longer in use, s must be freed, which means the allocated 7 bytes must be returned to the system.
g_free frees the memory.
Strings bounded by double quotes like "Hello." are string literals. They are an array of characters, but the contents of the array are not allowed to change. And they mustn't be freed. If you write a character in a string literal or free a string literal, the result is undefined. Maybe bad things will happen, for example, a segmentation fault error.
There's a difference between arrays and pointers when you initialize them with a string literal. If an array is initialized with a string literal, the array can be changed.
char a[]="Hello!";
a[1]='a';
g_print ("%s\n", a); /* Hallo will appear on your display. */The first line initializes an array a.
The initialization is not simple.
First, the compiler calculates the length of "Hello!".
It is seven because the string literal has '\0' at the end of it.
Then seven bytes memory is allocated in static memory or stack memory.
It depends on the class of the array, whether static or auto.
The memory is initialized with "Hello!".
So, the string in the array can be changed.
This program successfully displays `Hallo!.
The first line of the program above is the same as follows.
char a[] = {'H', 'e', 'l', 'l', 'o', '!', '\0'};If you define a pointer with string literal, you can't change the string pointed by the pointer.
char *a = "Hello";
*(a+1) = 'a'; /* This is illegal. */
g_print ("%s\n", a);The first line just assigns the address of the string literal to the variable a.
String literal is an array of characters but it's read-only.
It might be in the program code area or some other non-writable area.
It depends on the implementation of your compiler.
Therefore, the second line tries to write a char 'a' to the read-only memory and the result is undefined, for example, a segmentation error happens.
Anyway, don't write a program like this.
In conclusion, a string is an array of characters and it is placed in one of the following.
- Read-only memory. A string literal is read-only.
- Stack.
If the class of an array is
auto, then the array is placed in the stack. stack is writable. If the array is defined in a function, its default class isauto. The stack area will disappear when the function returns to the caller. - Static area.
If the class of an array is
static, then the array is placed in the static area. It keeps its value and remains for the life of the program. This area is writable. - Heap.
You can allocate and free memory for a string.
For allocation,
g_neworg_new0is used. For freeing,g_freeis used.
There are two ways to copy a string. First way is just copying the pointer.
char *s = "Hello";
char *t = s;Two pointers s and t points the same address.
Therefore, you can't modify t because t points a string literal, which is read-only.
Second way is creating memory and copying the string to the memory.
char *s = "Hello";
char *t = g_strdup (s);The function g_strdup allocates memory and initializes it with "Hello", then returns the pointer to the memory.
The function g_strdup is almost same as the function string_dup below.
#include <glib-object.h>
#include <string.h>
char *
string_dup (char *s) {
int length;
char *t;
if (s == NULL)
return NULL;
length = strlen (s) + 1;
t = g_new (char, length);
strcpy (t, s);
return t;
}If g_strdup is used, the two pointers s and t point different memories.
You can modify t because it is placed in the memory allocated from the heap area.
It is important to know the difference between assigning pointers and duplicating strings.
The qualifier const makes a variable won't change its value.
It can also be applied to an array.
Then, the elements of the array won't be changed.
const double pi = 3.1415926536;
const char a[] = "read only string";An array parameter in a function can be qualified with const to indicate that the function does not change the array.
In the same way, the return value (a pointer to an array or string) of a function can be qualified with const.
The caller mustn't modify or free the returned array or string.
char *
string_dup (const char *s) {
... ...
}
const char *
g_value_get_string (const GValue *value);The qualifier const indicates who is the owner of the string when it is used in the function of objects.
"Owner" is an object or a caller of the function that has the right to modify or free the string.
For example, g_value_get_string is given const GValue *value as an argument.
The GValue pointed by value is owned by the caller and the function doesn't change or free it.
The function returns a string qualified with const.
The returned string is owned by the object and the caller mustn't change or free the string.
It is possible that the string will be changed or freed by the object later.
The rest of this section is about TStr. TStr is a child of GObject and it holds a string. The string is a pointer and an array of characters. The pointer points the array. The pointer can be NULL. If it is NULL, TStr has no array. The memory of the array comes from the heap area. TStr owns the memory and is responsible to free it when it becomes useless. TStr has a string type property.
The header file tstr.h is as follows.
1 #pragma once
2
3 #include <glib-object.h>
4
5 #define T_TYPE_STR (t_str_get_type ())
6 G_DECLARE_DERIVABLE_TYPE (TStr, t_str, T, STR, GObject)
7
8 struct _TStrClass {
9 GObjectClass parent_class;
10 /* expect that descendants will override the setter */
11 void (*set_string) (TStr *self, const char *s);
12 };
13
14 TStr *
15 t_str_concat (TStr *self, TStr *other);
16
17 /* setter and getter */
18 void
19 t_str_set_string (TStr *self, const char *s);
20
21 char *
22 t_str_get_string (TStr *self);
23
24 /* create a new TStr instance */
25 TStr *
26 t_str_new_with_string (const char *s);
27
28 TStr *
29 t_str_new (void);- 6: Uses
G_DECLARE_DERIVABLE_TYPE. The TStr class is derivable and its child class will be defined later. - 8-12: TStrClass has one class method.
It is
set_stringmember of the TStrClass structure. This will be overridden by the child classTNumStr. Therefore, Both TStr and TNumStr hasset_stringmember in their classes but they point different functions. - 14-15: The public function
t_str_concatconnects two strings of TStr instances and returns a new TStr instance. - 18-22: Setter and getter.
- 25-29: Functions to create a TStr object.
The C file tstr.c for TStr is as follows.
1 #include "tstr.h"
2
3 enum {
4 PROP_0,
5 PROP_STRING,
6 N_PROPERTIES
7 };
8
9 static GParamSpec *str_properties[N_PROPERTIES] = {NULL, };
10
11 typedef struct {
12 char *string;
13 } TStrPrivate;
14
15 G_DEFINE_TYPE_WITH_PRIVATE(TStr, t_str, G_TYPE_OBJECT)
16
17 static void
18 t_str_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
19 TStr *self = T_STR (object);
20
21 /* The returned value of the function g_value_get_string can be NULL. */
22 /* The function t_str_set_string calls a class method, */
23 /* which is expected to rewrite in the descendant object. */
24 if (property_id == PROP_STRING)
25 t_str_set_string (self, g_value_get_string (value));
26 else
27 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
28 }
29
30 static void
31 t_str_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
32 TStr *self = T_STR (object);
33 TStrPrivate *priv = t_str_get_instance_private (self);
34
35 /* The second argument of the function g_value_set_string can be NULL. */
36 if (property_id == PROP_STRING)
37 g_value_set_string (value, priv->string);
38 else
39 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
40 }
41
42 /* This function just set the string. */
43 /* So, no notify signal is emitted. */
44 static void
45 t_str_real_set_string (TStr *self, const char *s) {
46 TStrPrivate *priv = t_str_get_instance_private (self);
47
48 if (priv->string)
49 g_free (priv->string);
50 priv->string = g_strdup (s);
51 }
52
53 static void
54 t_str_finalize (GObject *object) {
55 TStr *self = T_STR (object);
56 TStrPrivate *priv = t_str_get_instance_private (self);
57
58 if (priv->string)
59 g_free (priv->string);
60 G_OBJECT_CLASS (t_str_parent_class)->finalize (object);
61 }
62
63 static void
64 t_str_init (TStr *self) {
65 TStrPrivate *priv = t_str_get_instance_private (self);
66
67 priv->string = NULL;
68 }
69
70 static void
71 t_str_class_init (TStrClass *class) {
72 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
73
74 gobject_class->finalize = t_str_finalize;
75 gobject_class->set_property = t_str_set_property;
76 gobject_class->get_property = t_str_get_property;
77 str_properties[PROP_STRING] = g_param_spec_string ("string", "str", "string", "", G_PARAM_READWRITE);
78 g_object_class_install_properties (gobject_class, N_PROPERTIES, str_properties);
79
80 class->set_string = t_str_real_set_string;
81 }
82
83 /* setter and getter */
84 void
85 t_str_set_string (TStr *self, const char *s) {
86 g_return_if_fail (T_IS_STR (self));
87 TStrClass *class = T_STR_GET_CLASS (self);
88
89 /* The setter calls the class method 'set_string', */
90 /* which is expected to be overridden by the descendant TNumStr. */
91 /* Therefore, the behavior of the setter is different between TStr and TNumStr. */
92 class->set_string (self, s);
93 }
94
95 char *
96 t_str_get_string (TStr *self) {
97 g_return_val_if_fail (T_IS_STR (self), NULL);
98 TStrPrivate *priv = t_str_get_instance_private (self);
99
100 return g_strdup (priv->string);
101 }
102
103 TStr *
104 t_str_concat (TStr *self, TStr *other) {
105 g_return_val_if_fail (T_IS_STR (self), NULL);
106 g_return_val_if_fail (T_IS_STR (other), NULL);
107
108 char *s1, *s2, *s3;
109 TStr *str;
110
111 s1 = t_str_get_string (self);
112 s2 = t_str_get_string (other);
113 if (s1 && s2)
114 s3 = g_strconcat (s1, s2, NULL);
115 else if (s1)
116 s3 = g_strdup (s1);
117 else if (s2)
118 s3 = g_strdup (s2);
119 else
120 s3 = NULL;
121 str = t_str_new_with_string (s3);
122 if (s1) g_free (s1);
123 if (s2) g_free (s2);
124 if (s3) g_free (s3);
125 return str;
126 }
127
128 /* create a new TStr instance */
129 TStr *
130 t_str_new_with_string (const char *s) {
131 return T_STR (g_object_new (T_TYPE_STR, "string", s, NULL));
132 }
133
134 TStr *
135 t_str_new (void) {
136 return T_STR (g_object_new (T_TYPE_STR, NULL));
137 }- 3-9:
enumdefines a property id. The memberPROP_STRINGis the id of the "string" property. Only one property is defined here, so it is possible to define it withoutenum. However,enumcan be applied to define two or more properties and it is more common. The last memberN_PROPERTIESis two becauseenumis zero-based. An arraystr_propertieshas two elements sinceN_PROPERTIESis two. The first element isn't used and it is assigned with NULL. The second element will be assigned a pointer to a GParamSpec instance in the class initialization function. - 11-13: TStrPrivate is a C structure.
It is a private data area for TStr.
If TStr were a final type, then no descendant would exist and TStr instance could be a private data area.
But TStr is derivable so you can't store such private data in TStr instance that is open to the descendants.
The name of this structure is "<object name>Private" like
TStrPrivate. The structure must be defined beforeG_DEFINE_TYPE_WITH_PRIVATE. - 15:
G_DEFINE_TYPE_WITH_PRIVATEmacro. It is similar toG_DEFINE_TYPEmacro but it adds the private data area for the derivable instance. This macro expands to:- Declaration of
t_str_class_initwhich is a class initialization function. - Declaration of
t_str_initwhich is an instance initialization function. - Definition of
t_str_parent_classstatic variable. It points to the parent class of TStr. - The function call that adds private instance data to the type.
It is a C structure and its name is
TStrPrivate. - Definition of
t_str_get_type ()function. This function registers the type in its first call. - Definition of the private instance getter
t_str_get_instance_private ().
- Declaration of
- 17-28: The function
t_str_set_propertysets the "string" property and it is used byg_object_setfamily functions. It usest_str_set_stringfunction to set the private data with the copy of the string from the GValue. It is important becauset_str_set_stringcalls the class methodset_string, which will be overridden by the child class. Therefore, the behavior oft_str_set_propertyfunction is different between TStr and TNumStr, which is a child of TStr. The functiong_value_get_stringreturns the pointer to the string that GValue owns. So you need to duplicate the string and it is done in the functiont_str_set_string. - 30-40: The function
t_str_get_propertygets the "string" property and it is used byg_object_getfamily functions. It just givespriv->stringto the functiong_value_set_string. The variableprivpoints the private data area. The second argumentpriv->stringis owned by the TStr instance and the functiong_value_set_stringduplicates it to store in the GValue structure. - 44-51 The function
t_str_real_set_stringis the body of the class method and pointed byset_stringmember in the class. First, it gets the pointer to the private area witht_str_get_instance_privatefunction. If the instance holds a string, free it before setting it to a new string. It copies the given string and assigns it topriv->string. The duplication is important. Thanks to that, the address of the string is hidden from the out of the instance. - 53-61: The finalize function
t_str_finalizeis called when TStr instance is destroyed. The destruction process has two phases, "dispose" and "finalize". In the disposal phase, the instance releases instances. In the finalization phase, the instance does the rest of all the finalization like freeing memories. This function frees the stringpriv->stringif necessary. After that, it calls the parent's finalize method. This is called "chain up to its parent" and it will be explained later in this section. - 63-68: The instance initialization function
t_str_initassigns NULL topriv->string. - 70-81: The class initialization function
t_str_class_initoverridesfinalize,set_propertyandget_propertymembers. It creates the GParamSpec instance withg_param_spec_stringand installs the property withg_object_class_install_properties. It assignst_str_real_set_stringto the memberset_string. It is a class method and is expected to be replaced in the child class. - 84-101: Setter and getter.
The setter method
t_str_set_stringjust calls the class method. So, it is expected to be replaced in the child class. It is used by property set functiont_str_set_property. So the behavior of property setting will change in the child class. The getter methodt_str_get_stringjust duplicates the stringpriv->stringand return the copy. - 103-126: The public function
t_str_concatconcatenates the string ofselfandother, and creates a new TStr. - 129-137: Two functions
t_str_new_with_stringandt_str_newcreate a new TStr instances.
The "chain up to its parent" process is illustrated with the diagram below.
There are two classes, GObjectClass and TStrClass. Each class has their finalize methods (functions) pointed by the pointers in the class structures. The finalize method of TStrClass finalizes its own part of the TStr instance. At the end of the function, it calls its parent's finalize method. It is the finalize method of GObjectClass. It calls its own finalize function and finalizes the GObject private data.
If the GObjectClass has two or more descendant classes, the number of the finalize functions may be the same as the number of the descendants. And they are connected by "chain up to its parent" way.
- Use
G_DECLARE_DERIVABLE_TYPEmacro in the header file. You need to write a macro like#define T_TYPE_STR (t_str_get_type ())beforeG_DECLARE_DERIVABLE_TYPE. - Declare your class structure in the header file. The contents of the class are open to the descendants. Most of the members are class methods.
- Use
G_DEFINE_TYPE_WITH_PRIVATEin the C file. You need to define the private area beforeG_DEFINE_TYPE_WITH_PRIVATE. It is a C structure and the name is "<object name>Private" like "TStrPrivate". - Define class and instance initialization functions.

