设为首页 加入收藏

TOP

12.4 GTK+的面向对象机制
2013-10-12 06:48:06 来源: 作者: 【 】 浏览:101
Tags:12.4 GTK 面向 对象 机制

12.4  GTK+的面向对象机制

对于那些没有接触过面向对象语言的读者来说,本节的内容可能比较难以理解。不过没有关系,这并不会影响后面内容的掌握。介绍本节的内容只是为了简单地介绍一下GTK+中是如何模拟面向对象机制的。

面向对象编程语言(如C++、Java)把数据和对数据的操作封装在一起构成类,由类来产生对象,由对象来构建程序。类中对数据的操作由函数来完成,这种函数被称为成员函数或方法。面向对象语言通过继承、重载、多态等机制大大增强软件的可重用性和可维护性。C语言虽然不是面向对象语言,但GTK+以及建立在其上的GNOME库却使C语言模拟出了一些典型的面向对象机制,如封装、继承和多态。为了较好的理解GTK+程序,了解GTK+中的面向对象机制也是很有必要的。

对象的一个主要特性是将数据和对数据的操作封装在一起,受保护的私有数据只能通过成员函数才能访问和修改。GTK+使用C语言的结构体来模拟对象,虽然有些缺陷但基本模拟出了对象的基本特征。

有了对象作为基础,通过在对象中加入新的数据和对这些数据进行操作的函数,就实现了继承。被继承的类(类相当于一种自定义数据类型,由类来定义对象)称为父类或基类,由基础类派生出来的类称为子类或派生类。子类继承了父类的数据和对这些数据进行操作的成员函数,并加入了新的数据和成员函数,实现了对原有父类的重用和扩展,从而实现了可重用性和可扩展性。

GTK+中有一个类,它是所有其他类的父类,这个类是GtkObject。GTK+中最常用的按钮控件也是一个类,它继承自GtkObject。它与GtkObject的继承关系是:

GtkObject ->GtkWidget ->GtkContainer ->GtkBin ->GtkButton

使用C语言如何模拟继承呢?对象(类)是由结构体模拟的,每一个子类所在的结构体都包含了父类的结构体,子类结构体的第一个成员是其父类结构体,示例代码如下:

struct GtkObject {
...
};
struct GtkWidget {
GtkObject object;
...
};
struct GtkContainer {
GtkWidget widget;
...
};
struct GtkBin {
GtkContainer container;
...
};
struct GtkButton {
GtkBin bin;
...
};

从上述代码可以看到,每个子类都包含了其父类的所有数据,并且父类的数据位于子类结构体的开始。对于一个GtkButton 类型的button控件变量(它其实是一个指向GtkButton结构体的指针),通过宏GTKBIN(button)就得到了其父类(GTK+预定义的宏GTKBIN其实是进行了强制类型转换,把一个GtkButton类型的指针强制转化为GtkBin类型的指针)。例12-1中的GTK_OBJECT(button)就是进行了这样的转换。

为了便于理解,我们写一个测试程序,如例12-2所示。

例12-2  test.c

#include<stdio.h>
#include<stdlib.h>
#define FATHER(child) (struct Father *)(child)
void print1(int i)
{
printf("this is father and i = %d\n",i);
}
void print2(int i)
{
printf("this is child  and i = %d\n",i);
}
struct Father {
int  a;
void (*pointer1_to_function)(int);
};
struct Child {
struct Father f;
int b;
void (*pointer2_to_function)(int);
};
void father_member_funtion(struct Father *f,char *string)
{
printf("\n");
f->pointer1_to_function(f->a);
printf("%s\n\n",string);
}
int main()
{
struct Child *p_child;
p_child = (struct Child *)malloc(sizeof(struct Child));
p_child->f.a = 10;
p_child->f.pointer1_to_function = print1;
p_child->b   = 20;
p_child->pointer2_to_function   = print2;

p_child->pointer2_to_function(p_child->b);

struct Father *p_father = FATHER(p_child);
p_father->pointer1_to_function(p_father->a);

father_member_funtion(p_father,"hello");

return 0;
}

程序输出:

this is child and i = 20
this is father and i = 10
this is father and i = 10
hello

程序说明。

(1)结构体Father相当于GTK+中的父类,而结构体Child就相当于子类。结构体Father有一个成员变量和一个成员函数(实际上是一个指向函数的指针,函数指针的内容请参考第4章4.3.4指针和函数一节)。结构体Child在其头部包含了结构体Father,并增加了一个成员变量和一个成员函数(也是一个函数指针)。结构体Father和Child模拟了类,Child模拟继承了Father。

(2)程序定义了一个指向结构体Child的指针,并对Child中的成员进行了初始化。然后调用了结构体Child的成员函数pointer2_to_function。通过宏FATHER(p_child)将指针p_child强制转换为指向Father结构体的指针。事实上,p_child和p_father的值是一样的,它们都保存着结构体Child的首地址。宏FATHER(p_child)类似于例12-1程序gtk_container_add(GTK_CONTAINER (window),button)中的GTK_CONTAINER(window)。我们注意到,p_father调用了它自己的成员函数pointer1_to_function。

(3)father_member_funtion函数是类Father成员函数的另一种实现方法,这种方法避免了在结构体Father中保存函数指针。

GTK+定义了很多生成对象或对对象进行操作的函数。例如,下面就创建了一个对象:

GtkWidget *button;
button = gtk_button_new_with_label("label");

所有创建对象的函数在其名称上都有"new"这个词。函数gtk_button_new_with_label创建了一个显示文本的按钮。可以当作父类来对待所有子类(其实这就是面向对象语言中的多态),按钮button的真正类型是GtkButton,却也可以作为一个指向GtkWidget类型的指针。使用GtkWidget指针在编程上有很多好处,因为许多图形界面的操作函数都是在GtkWidget对象上进行操作的。

当调用一个函数对一个对象进行相关操作时,该对象的地址作为第一个参数传给函数。例如,显示按钮的函数:

gtk_widget_show(button);

button是一个指向GtkWidget结构体的指针,它也表示一个按钮控件。面向对象语言中子类可以调用父类的函数,在GTK+中只要使用一些宏将子类强制转换为父类即可。例如:

gtk_container_add(GTK_CONTAINER(window),button);

gtk_container_add函数是类GtkContainer的一个函数,而window代表一个窗口,它是GtkWindow类型的指针。GtkWindow是GtkContainer的子类,也就是GtkWindow继承自GtkContainer,当然它们都是从GtkObject派生出来的,GtkObject是它们的祖先:

GtkObject ->GtkWidget ->GtkContainer ->GtkBin ->GtkWindow


】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇12.5.2 使用table排列控件(1) 下一篇12.5.1 使用box排列控件

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: