GObject   継承

ホーム   C/C++チュートリアル  

継承

GObject のクラスの継承について学んでいきます。

Gobject で新しくクラスを作る場合、既存のクラスまたは、基底クラスの GObject を継承して作ります。

その際に、新し作るクラス用の構造体とインスタンス用の構造体の 2 つの構造体を作成し、 それをプログラムに登録する必要があります。

他のオブジェクト指向言語に慣れている方にとっては少し変な感覚ですが、GObject はあくまでもオブジェクト指向風のプログラミングができるライブラリです。 違和感のあるところも多少あります。

命名規則

GObject のオブジェクトは、名前空間と名前で命名される慣習になっています。GObjet は G という名前空間と Object という名前で構成されています。GtkWidget は、Gtk という名前空間と Widget という名前で構成 されています。

ここでは基底クラスである GObject を継承して、MyObj というオブジェクトを作っていきます。 My が名前空間で、Obj が名前です。

G_DEFINE_TYPE マクロ

クラスを作るときは、便利なマクロが用意されています。

define.c

#include <glib-object.h>

typedef struct _MyObjClass MyObjClass;
struct _MyObjClass
{
  GObjectClass parent_class;
};

typedef struct _MyObj MyObj;
struct _MyObj
{
  GObject parent;
  int num;
};

static void
my_obj_class_init (MyObjClass *class){}

static void
my_obj_init (MyObj *self) {}

G_DEFINE_TYPE (MyObj, my_obj, G_TYPE_OBJECT)

int
main (int argc, char **argv)
{
  GType type;
  MyObj *obj;
  
  type = my_obj_get_type ();
  if (type)
    g_print ("Registration successed  : %lx\n", type);
  else
    g_print ("Registration failed.\n");
  
  obj = g_object_new (my_obj_get_type(), NULL);
  if (obj)
    g_print ("Instantiation successed : %p\n", obj);
  else
    g_print ("Instantiation failed.\n");
  
  g_object_unref (obj);
  
  return 0;
}

クラスの構造体では、スーパークラスのメンバーのみを定義します。

インスタンスの構造体では、最初にスーパークラスのメンバーを定義して、 そのあとに、他のメンバーを登録していきます。

ビルドと実行

以下のとおりです。Windows の場合は通常版でも ARM64版 でも mingw64 で行ってください。


// ビルド
cc define.c -o define `pkg-config --cflags --libs gtk4`

// 実行
./define

// 結果
Registration successed  : 600003991620
Instantiation successed : 0x600001d93e40

G_DECLARE_FINAL_TYPE マクロ

ファイナルクラス (最終型クラス) を作るマクロです。ファイナルクラスとはサブクラスを持たないクラスのことです。

実際にプログラミングをしていて、サブクラスを持たないクラスを作ることは多いと思います。このマクロはそのようなときに使います。

このマクロを使う場合には次の 3 つの注意点があります。

  1. #include や #define が終わった、最初の行で使わなければならない
  2. クラス用の構造体は作ってはいけない。インスタンスの構造体だけを作る
  3. G_DEFINE_TYPE マクロも併せて使わなければならない

なお、サブクラスを持てる、G_DECLARE_DERIVABLE_TYPE というマクロもあります。

declare.c


#include <glib-object.h>

// G_DECLARE_FINAL_TYPE は #include や #define などが終わったこの位置に記述しなければなりません。
G_DECLARE_FINAL_TYPE (MyObj, my_obj, MY, OBJ, GObject)

struct _MyObj {
  GObject parent;
  int num;
};

static void
my_obj_class_init (MyObjClass *class) {}

static void
my_obj_init (MyObj *self) {}

// G_DEFINE_TYPE マクロの記述も必要です
G_DEFINE_TYPE (MyObj, my_obj, G_TYPE_OBJECT)

int
main (int argc, char **argv) {
  GType type;
  MyObj *obj;

  type = my_obj_get_type ();
  if (type)
    g_print ("Registration successed. The type is %lx.\n", type);
  else
    g_print ("Registration failed.\n");

  obj = g_object_new (my_obj_get_type (), NULL);
  if (obj)
    g_print ("Instantiation successed. The instance address is %p.\n", obj);
  else
    g_print ("Instantiation failed.\n");

  if (MY_IS_OBJ (obj))	// G_DECLARE_FINAL_TYPE マクロを使った場合だけ使えるマクロです
    g_print ("obj is MyObj instance.\n");
  else
    g_print ("obj is not MyObj instance.\n");

  if (G_IS_OBJECT (obj))	// G_DEFINE_TYPE マクロだけで使った場合も使えるマクロです
    g_print ("obj is GObject instance.\n");
  else
    g_print ("obj is not GObject instance.\n");
  g_object_unref (obj);

  return 0;
}

ビルドと実行


// ビルド
cc declare.c -o declare `pkg-config --cflags --libs gtk4`

// 実行
./declare

// 結果
Registration successed. The type is 6000037f5620.
Instantiation successed. The instance address is 0x6000013f0360.
obj is MyObj instance.
obj is GObject instance.

マクロを使わない例

マクロを使わない例も一応揚げておきます。

nonmacro.c


#include <glib-object.h>

typedef struct _MyObjClass MyObjClass;
struct _MyObjClass
{
  GObjectClass parent_class;
};

typedef struct _MyObj MyObj;
struct _MyObj
{
  GObject parent;
  int num;
};

static void
my_obj_class_init (MyObjClass *class){}

static void
my_obj_init (MyObj *self) {}

GType
my_obj_get_type (void)
{
  static GType type = 0;
  GTypeInfo info;
  
  if (type == 0)
  {
    info.class_size = sizeof (MyObjClass);
    info.base_init = NULL;
    info.base_finalize = NULL;
    info.class_init = (GClassInitFunc) my_obj_class_init;
    info.class_finalize = NULL;
    info.class_data = NULL;
    info.instance_size = sizeof (MyObj);
    info.n_preallocs = 0;
    info.instance_init = (GInstanceInitFunc) my_obj_init;
    info.value_table = NULL;
    type = g_type_register_static (G_TYPE_OBJECT, "MyObj", &info, 0);
  }
  
  return type;
}

int
main (int argc, char **argv)
{
  GType type;
  MyObj *obj;
  
  type = my_obj_get_type ();
  if (type)
    g_print ("Registration successed  : %lx\n", type);
  else
    g_print ("Registration failed.\n");
  
  obj = g_object_new (my_obj_get_type (), NULL);
  if (obj)
    g_print ("Instantiation successed : %p\n", obj);
  else
    g_print ("Instantiation failed.\n");
  
  g_object_unref (obj);
  
  return 0;
}

コード説明

  1. クラスの構造体では、スーパークラスのメンバーのみを定義しています。
  2. インスタンスの構造体では、最初にスーパークラスのメンバーを定義して、 そのあとに、値のメンバーを登録していきます。
  3. static void my_obj_class_init (MyObjClass *class) {}
    クラスを初期化する関数を定義します。実装はありません。
  4. static void my_obj_init (MyObj *self) {}
    インスタンスを初期化する関数を定義します。実装はありません。
  5. GType my_obj_get_type (void) {...}
    プログラムにクラスを登録するさいに必要となる情報を定義しています。 作成するクラスに合わせて各データを変更してください。 この部分はひな形化していますので、実際には後述するマクロを使って定義します。

ビルドと実行

以下のとおりです。Windows の場合は通常版でも ARM64版 でも mingw64 で行ってください。


// コンパイル
cc nonmacro.c -o nonmacro `pkg-config --cflags --libs gtk4`

// 実行
./nonmacro

// 結果
Registration successed  : 6000000e9440
Instantiation successed : 0x6000024e9e60


45 visits
Posted: Mar. 22, 2025
Update: Mar. 23, 2025

ホーム   C/C++チュートリアル   目次