Rectangle型別宣告如下:
rectangle.h
#ifndef __GOBJECT_TEST_RECTANGLE_CLASS_INCLUDED__ #define __GOBJECT_TEST_RECTANGLE_CLASS_INCLUDED__ #include <glib-object.h> #include "shape.h" G_BEGIN_DECLS #define RECTANGLE_TYPE (rectangle_get_type()) #define IS_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), RECTANGLE_TYPE)) #define RECTANGLE(object) (G_TYPE_CHECK_INSTANCE_CAST((object), RECTANGLE_TYPE, Rectangle)) #define RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RECTANGLE_TYPE, RectangleClass)) #define IS_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RECTANGLE_TYPE)) #define RECTANGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RECTANGLE_TYPE, RectangleClass)) typedef struct _Rectangle Rectangle; typedef struct _RectangleClass RectangleClass; typedef struct _RectanglePrivate RectanglePrivate; struct _Rectangle { Shape parent; RectanglePrivate* priv; }; struct _RectangleClass { ShapeClass parent; }; GType rectangle_get_type(); Rectangle* rectangle_new(guint width, guint height); void rectangle_free(Rectangle* rect); void rectangle_set_width(Rectangle* rect, guint width); void rectangle_set_height(Rectangle* rect, guint height); guint rectangle_get_width(Rectangle* rect); guint rectangle_get_height(Rectangle* rect); G_END_DECLS #endif /* __GOBJECT_TEST_RECTANGLE_CLASS_INCLUDED__ */rectangle.c
#include "rectangle.h" struct _RectanglePrivate { guint width; guint height; }; #define RECTANGLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \ RECTANGLE_TYPE, RectanglePrivate)) static void rectangle_base_init(RectangleClass* klass); static void rectangle_base_finalize(RectangleClass* klass); static void rectangle_class_init(RectangleClass* klass); static void rectangle_init(Rectangle* rect); static guint rectangle_calculate_area(Rectangle* self); GType rectangle_get_type() { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(RectangleClass), (GBaseInitFunc)rectangle_base_init, (GBaseFinalizeFunc)rectangle_base_finalize, (GClassInitFunc)rectangle_class_init, NULL, NULL, sizeof(Rectangle), 0, (GInstanceInitFunc)rectangle_init, NULL }; type = g_type_register_static(SHAPE_TYPE, "Rectangle", &info, 0); } return type; } static void rectangle_base_init(RectangleClass* klass) { g_print("Rectangle(0x%08x)::base::initialize\n", (guint)klass); } static void rectangle_base_finalize(RectangleClass* klass) { g_print("Rectangle(0x%08x)::base::finalize\n", (guint)klass); } static void rectangle_class_init(RectangleClass* klass) { g_print("Rectangle(0x%08x)::class::initialize\n", (guint)klass); SHAPE_CLASS(klass)->calculateArea = (void*)rectangle_calculate_area; g_type_class_add_private(klass, sizeof(RectanglePrivate)); } static void rectangle_init(Rectangle* self) { RectanglePrivate* priv; g_print("Rectangle(0x%08x)::instance::initialize\n", (guint)self); self->priv = priv = RECTANGLE_GET_PRIVATE(self); priv->width = 0; priv->height = 0; } Rectangle* rectangle_new(guint width, guint height) { Rectangle* rect; rect = g_object_new(RECTANGLE_TYPE, NULL); g_assert(rect != NULL); rect->priv->width = width; rect->priv->height = height; g_print("Rectangle(0x%08x, %ux%u) is created.\n", (guint)rect, rect->priv->width, rect->priv->height); return rect; } void rectangle_free(Rectangle* rect) { g_assert(rect != NULL); g_return_if_fail(IS_RECTANGLE(rect)); g_object_unref(G_OBJECT(rect)); } static guint rectangle_calculate_area(Rectangle* self) { g_print("Rectangle(0x%08x)::calculateArea\n", (guint)self); return self->priv->width * self->priv->height; } void rectangle_set_width(Rectangle* rect, guint width) { g_return_if_fail(IS_RECTANGLE(rect)); rect->priv->width = width; } void rectangle_set_height(Rectangle* rect, guint height) { g_return_if_fail(IS_RECTANGLE(rect)); rect->priv->height = height; } guint rectangle_get_width(Rectangle* rect) { g_assert(IS_RECTANGLE(rect)); return rect->priv->width; } guint rectangle_get_height(Rectangle* rect) { g_assert(IS_RECTANGLE(rect)); return rect->priv->height; }Circle型別宣告如下:
circle.h
#ifndef __GOBJECT_TEST_CIRCLE_CLASS_INCLUDED__ #define __GOBJECT_TEST_CIRCLE_CLASS_INCLUDED__ #include <glib-object.h> #include "shape.h" G_BEGIN_DECLS #define CIRCLE_TYPE (circle_get_type()) #define IS_CIRCLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), CIRCLE_TYPE)) #define CIRCLE(object) (G_TYPE_CHECK_INSTANCE_CAST((object), CIRCLE_TYPE, Circle)) #define CIRCLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), CIRCLE_TYPE, CircleClass)) #define IS_CIRCLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), CIRCLE_TYPE)) #define CIRCLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), CIRCLE_TYPE, CircleClass)) typedef struct _Circle Circle; typedef struct _CircleClass CircleClass; typedef struct _CirclePrivate CirclePrivate; struct _Circle { Shape parent; CirclePrivate* priv; }; struct _CircleClass { ShapeClass parent; }; GType circle_get_type(); Circle* circle_new(guint radius); void circle_free(Circle* circle); void rectangle_set_radius(Circle* circle, guint radius); guint rectangle_get_radius(Circle* circle); G_END_DECLS #endif /* __GOBJECT_TEST_CIRCLE_CLASS_INCLUDED__ */
circle.c
#include "circle.h" struct _CirclePrivate { guint radius; }; #define CIRCLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \ CIRCLE_TYPE, CirclePrivate)) static void circle_base_init(CircleClass* klass); static void circle_base_finalize(CircleClass* klass); static void circle_class_init(CircleClass* klass); static void circle_init(Circle* circle); static guint circle_calculate_area(Circle* self); GType circle_get_type() { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(CircleClass), (GBaseInitFunc)circle_base_init, (GBaseFinalizeFunc)circle_base_finalize, (GClassInitFunc)circle_class_init, NULL, NULL, sizeof(Circle), 0, (GInstanceInitFunc)circle_init, NULL }; type = g_type_register_static(SHAPE_TYPE, "Circle", &info, 0); } return type; } static void circle_base_init(CircleClass* klass) { g_print("Circle(0x%08x)::base::initialize\n", (guint)klass); } static void circle_base_finalize(CircleClass* klass) { g_print("Circle(0x%08x)::base::finalize\n", (guint)klass); } static void circle_class_init(CircleClass* klass) { g_print("Circle(0x%08x)::class::initialize\n", (guint)klass); SHAPE_CLASS(klass)->calculateArea = (void*)circle_calculate_area; g_type_class_add_private(klass, sizeof(CirclePrivate)); } static void circle_init(Circle* self) { CirclePrivate* priv; g_print("Circle(0x%08x)::instance::initialize\n", (guint)self); self->priv = priv = CIRCLE_GET_PRIVATE(self); priv->radius = 0; } Circle* circle_new(guint radius) { Circle* circle; circle = g_object_new(CIRCLE_TYPE, NULL); g_assert(circle != NULL); circle->priv->radius = radius; g_print("Circle(0x%08x, r=%u) is created.\n", (guint)circle, circle->priv->radius); return circle; } void circle_free(Circle* circle) { g_assert(circle != NULL); g_return_if_fail(IS_CIRCLE(circle)); g_object_unref(G_OBJECT(circle)); } static guint circle_calculate_area(Circle* self) { g_print("Circle(0x%08x)::calculateArea\n", (guint)self); return self->priv->radius * self->priv->radius * 3; } void circle_set_radius(Circle* circle, guint radius) { g_return_if_fail(IS_CIRCLE(circle)); circle->priv->radius = radius; } guint circle_get_width(Circle* circle) { g_assert(IS_CIRCLE(circle)); return circle->priv->radius; }上面的程式碼有幾個比較值得注意的地方。首先,由於Rectangle與Circle計算面積的方法不同,所以他們都必須覆寫(override)父類別(parent class)的calculateArea()方法。覆寫的最簡單方式就是在型別初始化時就設定正確的函數指標(function pointer),所以依據上一篇提過的觀念,於GClassInit()被喚起時進行設定是較正確的作法,也是官方文件建議的作法。當然,也可以選擇在GBaseInit()被喚起時設定函數指標(function pointer),但採取這種方式會增加些許程式碼撰寫的工程,觀念上亦與GObject system的架構略有不符。
... ... ... ... static void circle_class_init(CircleClass* klass) { g_print("Circle(0x%08x)::class::initialize\n", (guint)klass); SHAPE_CLASS(klass)->calculateArea = (void*)circle_calculate_area; g_type_class_add_private(klass, sizeof(CirclePrivate)); } ... ... ... ...此外,上面的程式碼亦導入RectanglePrivate與CirclePrivate結構,他們在標頭檔(header file)裡只有宣告沒有實作,這是一個常用的設計手法,透過這種方式,類別的資料內容可以被封裝保護得更完整,缺點是會增加程式撰寫的難度。GObject system建議但並不強迫使用這種作法,如果在類別宣告裡使用此作法,GObject system對類別私有資料的命名慣例為priv(不要使用 private,因為此名稱為C++的關鍵字)。至於私有資料結構的產生,只要於GClassInit時呼叫
g_type_class_add_private()
,爾後在該型別的物件初始化時期,GObject將自動加以配置;當該物件銷毀時,也會連帶註銷。至於物件可透過G_TYPE_INSTANCE_GET_PRIVATE()
取得屬於該私有資料的指標。... ... ... ... static void circle_class_init(CircleClass* klass) { g_print("Circle(0x%08x)::class::initialize\n", (guint)klass); SHAPE_CLASS(klass)->calculateArea = (void*)circle_calculate_area; g_type_class_add_private(klass, sizeof(CirclePrivate)); } static void circle_init(Circle* self) { CirclePrivate* priv; g_print("Circle(0x%08x)::instance::initialize\n", (guint)self); self->priv = priv = CIRCLE_GET_PRIVATE(self); priv->radius = 0; } ... ... ... ...最後,我們可以增添一個介面叫shape_calculate_area(),針對不同的Sahpe子類別(subclass)呼叫相應的calculateArea()。
... ... ... ... guint shape_calculate_area(Shape* shape) { if (!IS_SHAPE(shape)) return 0; if (SHAPE_GET_CLASS(shape)->calculateArea == NULL) { g_print("Shape::calculateArea is not implemented\n"); return 0; } return SHAPE_GET_CLASS(shape)->calculateArea(shape); } ... ... ... ...最後,透過一個簡單的測試程式,確認是否calculateArea()有被正確派送(dispatch)並處理。
測試碼:
#include <glib.h> #include "shape.h" #include "rectangle.h" #include "circle.h" int main(int argc, char** argv) { Rectangle* rect; Circle* circle; g_type_init(); rect = rectangle_new(4, 5); g_print("Area of Rectangle(0x%08x) is %u.\n", (guint)rect, shape_calculate_area(SHAPE(rect))); rectangle_free(rect); circle = circle_new(6); g_print("Area of Circle(0x%08x) is %u.\n", (guint)circle, shape_calculate_area(SHAPE(circle))); circle_free(circle); return 0; }測試碼執行結果:
完整原始碼下載
No comments:
Post a Comment