文本文件编辑器(tfe)的新版本将在本节和以下四节中编写。它是tfe5。与之前的版本相比,有很多变化。它们位于两个目录中,src/tfe5和src/tfetextview。
1 封装
我们将C源文件分为两部分。但就封装而言,这还不够。
- tfe.c包含除Tfe TextView之外的所有内容。它至少应该分为两部分,tfeapplication.c和tfenotebook.c。
- 头文件也需要组织。
然而,首先,我想关注TfeTextView对象。它是GtkTextView的一个子对象,其中有一个新的成员文件。重要的是管理file指向的Gfile对象。
- 当你创建(或初始化)TfeTextView,对于GFile什么是必须的?
- 当你销毁TfeTextView,对于GFile什么是必须的?
- TfeTextView是否应该自己读/写文件?
- 它如何与其他的对象交流?
在考虑类、实例和信号之前,你至少需要知道它们。我将在本节和下一节中解释它们。之后我会解释:
- 组织功能(Organizing functions)
- 如何使用GtkFileChooserDialog
2 GObject and its children
GObject及其子对象都是对象,它们既有class C结构,也有object C结构。首先,考虑实例。一个实例是具有对象结构的内存。下面是TfeTextView的结构。
/* This typedef statement is automatically generated by the macro G_DECLARE_FINAL_TYPE */
typedef struct _TfeTextView TfeTextView;
struct _TfeTextView {
GtkTextView parent;
GFile *file;
};
该结构的成员如下:
- 父类的类型是GtkTextView,这是C结构体。它在gtktextview.h中声明。GtkTextView是TfeTextView的父类。
- file是一个GFile类型的指针。如果没有文件对应于TfeTextView实例,它可以是NULL。
您可以在GTK和GLib的源文件中找到父类对象结构的声明。下面的代码是从源文件中提取的(不完全相同)。
typedef struct _GObject GObject;
typedef struct _GObject GInitiallyUnowned;
struct _GObject
{
GTypeInstance g_type_instance;
volatile guint ref_count;
GData *qdata;
};
typedef struct _GtkWidget GtkWidget;
struct _GtkWidget
{
GInitiallyUnowned parent_instance;
GtkWidgetPrivate *priv;
};
typedef struct _GtkTextView GtkTextView;
struct _GtkTextView
{
GtkWidget parent_instance;
GtkTextViewPrivate *priv;
};
在每个结构中,其父元素声明在成员元素的顶部。所以,所有的父类都包含在子对象中。TfeTextView的结构如下图所示。

派生类(父类)都有它们自己的私有数据区,这些私有数据区不包含在上述结构中。例如,GtkWidget的私有数据为GtkWidgetPrivate (C结构体)。
注意声明(declarations)不是定义(definitions)。因此,在声明C结构体时不会分配内存。在调用tfe_text_view_new函数时,会从堆区域为它们分配内存。同时,对于TfeTetView父类的私有区域也会被分配。它们从TfeTextView隐藏,不能直接访问它们。创建的内存称为实例(instance)。创建TfeTextView实例时,会给它三个数据区域。
- 实例(C结构体)。
- GtkWidgetPrivate结构体。
- GtkTextViewPrivate结构体。
TfeTextView函数只能访问其实例。GtkWidgetPrivate和GtkTextViewPrivate被父类的函数使用。请看下面的例子。
GtkWidget *tv = tfe_text_view_new ();
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
父函数gtk_text_view_get_buffer访问GtkTextViewPrivate数据(由tv拥有)。私有区域中有一个指向GtkBuffer的指针,该函数返回该指针。(实际的行为有点复杂。)
TfeTextView实例像这样继承了父类函数。
每次调用tfe_text_view_new函数时,都会创建一个TfeTextView实例。因此,可以存在多个TfeTextView实例。
3 初始化TfeTextView实例
函数tfe_text_view_new创建了一个新的TfeTextView实例。
1 GtkWidget *
2 tfe_text_view_new (void) {
3 return GTK_WIDGET (g_object_new (TFE_TYPE_TEXT_VIEW, "wrap-mode", GTK_WRAP_WORD_CHAR, NULL));
4 }
调用这个函数时,会创建并初始化一个TfeTextView实例。初始化过程如下。
- 当创建实例时,也创建了GtkWidgetPrivate和GtkTextViewPrivate结构体。
- 在TfeTextView实例中初始化GObject (GInitiallyUnowned)部分。
- 在TfeTextView实例和GtkWidgetPrivate结构中初始化GtkWidget部分(第一个priv)。
- 初始化GtkTextView部分(第二个priv)在TfeTextView实例和GtkTextViewPrivate结构。
- 在TfeTextView实例中初始化TfeTextView部分(文件)。
第二到第四步由g_object_init、gtk_widget_init和gtk_text_view_init完成。它们由系统自动调用,你不需要关心它们。第四步是由tfetextview.c中的函数tfe_text_view_init完成的。
1 static void
2 tfe_text_view_init (TfeTextView *tv) {
3 tv->file = NULL;
4 }
这个函数只是将tv->file初始化为NULL。
4 方法和类
在Gtk中,所有从GObject派生的对象都有类和实例(抽象对象除外)。实例是C结构体的内存,在前两个小节中描述过。每个对象可以有多个实例。这些实例具有相同的结构。实例只有数据。因此,它没有定义对象的行为。我们至少需要两样东西。一个是函数,另一个是类方法。
你已经见过很多函数了。例如,
- TfeTextView *tfe_text_view_new (void);是一个创建TfeTextView实例的函数。
- GtkTextBuffer *gtk_text_view_get_buffer (GtkTextView *textview)是一个从GtkTextView获取GtkTextBuffer的函数。
函数是公有的,这意味着它们可以被其他对象使用。它们类似于面向对象语言中的公共方法。
类(C结构体)主要由指向函数指针组成。这些函数被称为类方法,由对象本身或其后代对象使用。例如,GObject类在GLib源文件的GObject .h中声明。
1 typedef struct _GObjectClass GObjectClass;
2 typedef struct _GObjectClass GInitiallyUnownedClass;
3
4 struct _GObjectClass
5 {
6 GTypeClass g_type_class;
7
8 /*< private >*/
9 GSList *construct_properties;
10
11 /*< public >*/
12 /* seldom overridden */
13 GObject

文章详细介绍了TfeTextView的新版本,包括其封装改进,如何组织C源文件和头文件,以及GObject类和子类的结构。重点讨论了TfeTextView实例的初始化,如创建、设置GFile对象和管理引用计数。文章还阐述了对象的销毁过程,特别是dispose和finalize方法在解除对象间依赖和释放资源中的作用。
最低0.47元/天 解锁文章
1384

被折叠的 条评论
为什么被折叠?



