使用C语言进行面向对象的开发--GObject入门[9]

转载请注明出处 blog.csdn.net/pingf0 或www.cnblogs.com/pingf

 

PART 9
        注:这一部分的大部分内容源自google wallpaper上对gtk mail archive上关于gobject的一些翻译,这里只是引用了下【链接见前文】,因为原文对此部分的描述还是相当不错的,至少比我觉的自己要写还写不了这么好。
    属性
JAVA,C#好像有着这么一个概念【从语言层面是原生的】,但是标准的C++还是没有的【微软的C++/CLI实现了属性,但微软的并不是标准的,另外大名鼎鼎的QT也实现了属性这一概念】
属性到底是什么,个人感觉跟成员变量差不了多少,无非是我们可以明确的声明他们的可读可写。。。。另外如果把它当成成员变量,那么访问时可以像成员变量一样容易,但实际底层却调用专门的函式来对其访问。
个人感觉:如果程序员对自己的程序都是明了的,属性这个东西根本就没必要!【哎,又大放厥词啦。。。。。。Orz】从使用的角度而言,带来的方便并不是很大,但从代码的角度却要增加不小的开销。
用C++我们可以使用模板配合宏来模拟属性,并且这种模拟在声明的时候麻烦了些。但如果用C来实现,仅靠函式指针和宏就有些力不从心了,至少一步一步实现起来会相当麻烦。GObject对于属性的实现还是比较不错的,使用起来也还算方便,这得益于实现了泛型信号的机制。
下面要转载一部分文字,因为感觉对这一块的描述相当的不错【不过是翻译的】

GValues

C是一门强类型语言,也就是说变量声明的类型必须和它被使用的方式保持一致,否则编译器就会报错。这是一件好事,它使得程序编写起来更迅速,帮助我们发现可能会导致系统崩溃或者不安全的因素。但这又是件坏事,因为实际上程序员活在一个很难什么事都保持严格的世界上,而且我们也希望声明的类型能够具备多态的能力 也就是说类型能够根据上下文来改变它们自己的特性。通过C语言的转型我们可以获得一些多态的能力,如上面所讨论过的继承。然而,当使用无类型指针作为参数传递给函数时,可能问题会比较多。幸运的是,类型系统给了我们另外一个C语言没有的工具:GType

让我们更清楚的描述一下问题吧。我需要一种数据类型,可以实现一个可以容纳多类型元素的链表,我想为这个链表编写一些接口,可以不依赖于任何特定的类型,并且不需要我为每种数据类型声明一个多余的函数。这种接口必然能涵盖多种类型,所以我们称它为GValueGeneric Value,泛型)。该如何实现这样一个类型呢?

我们创建了封装这种类型的结构体,它具有两个成员域:所有基础类型的联合(union),和表示保存在这个union中的值的GType。这样我们就可以将值的类型隐藏在GValue中,并且通过检查对GValue的操作来保证类型是安全的。这样还减少了多余的以类型为基础的操作接口(如get_intset_float...),统一换成了g_value_*的形式。

细心的读者会发现每个GValue都占据了最大的基础类型的内存大小(通常是8字节),再加上GType自己的大小。是的,GValues在空间上不是最优的,包含了不小的浪费,因此不应该被大量的使用它。它最常被用在定义一些泛型的API上。

属性是如何工作的这一点稍稍超出了我们要讨论的范围,但是这对于理解属性本身还是很有帮助的。

/* 让我们使用GValue来复制整型数据! */ 

#define g_value_new(type) g_value_init (g_new (GValue, 1), type) 

 

GValue *a = g_value_new (G_TYPE_UCHAR); 

GValue *b = g_value_new (G_TYPE_INT); 

int c = 0; 

 

g_value_set_uchar (a, 'a'); 

g_value_copy (a, b); 

 

c = g_value_get (b); 

g_print ("w00t: %d\n", c); 

 

g_free (a); 

g_free (b);

设计

我们已经在上面接触过属性了,对它们有了初步的认识,现在我们将继续来了解一下设计它们的最初动机。 要编写一个泛型的属性设置机制,我们需要一个将其参数化的方法,以及与实例结构体中的成员变量名查重的机制。从外部上看,我们希望使用C字符串来区分属性和公有API,但是内部上来说,这样做会严重的影响效率。因此我们枚举化了属性,使用索引来标识它们。

上面提过属性规格,在Glib中被称作!GParamSpec,它保存了对象的gtype,对象的属性名称,属性枚举ID,属性默认值,边界值等,类型系统用!GParamSpec来将属性的字符串名转换为枚举的属性IDGParamSpec也是一个能把所有东西都粘在一起的大胶水。

当我们需要设置或者获取一个属性的值时,传入属性的名字,并且带上GValue用来保存我们要设置的值,调用g_object_set/get_propertyg_object_set_property函数将在GParamSpec中查找我们要设置的属性名称,查找我们对象的类,并且调用对象的set_property方法。这意味着如果我们要增加一个新的属性,就必须要覆盖默认的set/get_property方法。而且基类包含的属性将被它自己的set/get_property方法所正常处理,因为!GParamSpec就是从基类传递下来的。最后,应该记住,我们必须事先通过对象的class_init方法来传入GParamSpec参数,用于安装上属性!

假设我们已经有了如上一节所描述的那样一个可用的框架,那么现在让我们来为SomeObject加入处理属性的代码吧!

代码(头文件)

a. 除了我们增加了两个属性外,其余同上面的一样。

/* “实例结构体”定义所有的数据域,实例对象将是唯一的 */ 

typedef struct _SomeObject SomeObject; 

struct _SomeObject 

{  

        GObject         parent_obj; 

 

        /* 新增加的属性 */ //ß其实还是成员变量,属性机制是额外提供的

        int             a; 

        float           b; 

 

        /* 下面是一些数据 */ 

};

代码(源文件)

a. 创建一个枚举类型用来内部记录属性。

enum 

{  

        OBJECT_PROPERTY_A = 1 << 1; 

        OBJECT_PROPERTY_B = 1 << 2; 

};

b. 实现新增的处理属性的函数。

void    some_object_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) 

{  

        SomeObject *self = SOME_OBJECT (object); 

 

        switch (property_id) 

        {  

                case OBJECT_PROPERTY_A: 

                        g_value_set_int (value, self-> a); 

                        break; 

 

                case OBJECT_PROPERTY_B: 

                        g_value_set_float (value, self-> b); 

                        break; 

 

                default: /* 没有属性用到这个ID!! */ 

        } 

} 

 

void    some_object_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) 

{  

        SomeObject *self = SOME_OBJECT (object); 

 

        switch (property_id) 

        {  

                case OBJECT_PROPERTY_A: 

                        self-> a = g_value_get_int (value); 

                        break; 

 

                case OBJECT_PROPERTY_B: 

                        self-> b = g_value_get_float (value); 

                        break; 

 

                default: /* 没有属性用到这个ID!! */ 

        } 

}

c. 覆盖继承自基类的set/get_property方法,并且传入GParamSpecs

/* 这里是我们覆盖函数的地方 */ 

void    some_object_class_init          (gpointer g_class, gpointer class_data) 

{  

        GObjectClass    *this_class     = G_OBJECT_CLASS (g_class); 

        GParamSpec      *spec; 

         

        this_class-> constructor        = &some_object_constructor; 

        this_class-> dispose            = &some_object_dispose; 

        this_class-> finalize           = &some_object_finalize; 

 

        this_class-> set_property       = &some_object_set_property; 

        this_class-> get_property       = &some_object_get_property; 

 

        spec = g_param_spec_int 

        ( 

                "property-a",                           /* 属性名称 */ 

                "a",                                    /* 属性昵称 */ 

                "Mysterty value 1",                     /* 属性描述 */ 

                5,                                      /* 属性最大值 */ 

                10,                                     /* 属性最小值 */ 

                5,                                      /* 属性默认值 */ 

                G_PARAM_READABLE |G_PARAM_WRITABLE      /* GParamSpecFlags */ 

        ); 

        g_object_class_install_property (this_class, OBJECT_PROPERTY_A, spec); 

 

        spec = g_param_spec_float 

        ( 

                "property-b",                           /* 属性名称 */ 

                "b",                                    /* 属性昵称 */ 

                "Mysterty value 2"                      /* 属性描述 */ 

                0.0,                                    /* 属性最大值 */ 

                1.0,                                    /* 属性最小值 */ 

                0.5,                                    /* 属性默认值 */ 

                G_PARAM_READABLE |G_PARAM_WRITABLE      /* GParamSpecFlags */ 

        ); 

        g_object_class_install_property (this_class, OBJECT_PROPERTY_B, spec); 

}

   最后再啰嗦两句
1.我们设置属性,就会调用指定的set函式去设置相应的成员变量,获取属性时则调用get函式将对应的成员变量当前的值返回。
而成员变量本身的修改和获得,跟GObject属性机制本身是不想干的。
2.对于继承下来的属性,是遵循从底至顶的原则的。
3.如果对属性的设置不在合理范围之内,将返回一个错误。
4.接口里面也可以定义属性,通常在base_init里面实现。
5,另外,我们可以通过g_object_set和g_object_get来获取和设置一个或多个属性。

 

转载于:https://www.cnblogs.com/pingf/archive/2009/12/13/1623098.html

你可能感兴趣的:(使用C语言进行面向对象的开发--GObject入门[9])