GObject OOP 语法

edg*_*eis 3 c gtk oop gobject

我正在寻找 GObject 备忘单,了解常见的 OOP 概念如何映射到 GObject 的设施。例如考虑:

AnyGObject *o;
o = anygobject_new();
Run Code Online (Sandbox Code Playgroud)

现在,约定是什么...

  • 调用一个方法
  • 调用基类声明的方法
  • 调用该类正在实现的接口声明的方法
  • 调用类方法
  • 调用基类声明的类方法
  • 调用虚方法
  • 转换为基类
  • 转换为派生类
  • 转换为类正在实现的接口
  • 测试对象是否属于特定类型
  • ...

说明:GObject 参考手册GObject HowTo详细解释了如何创建新类(类结构、对象结构、私有结构、各种宏、约定)。将这些设施结合在一起就可以实现 OOP。然而似乎没有关于如何一致使用它们的教程。

and*_*abs 5

这个答案假设您正在使用 C。其他(通常是面向对象的)语言具有特殊的绑定,以使使用 GObject 看起来更自然。

如果您使用过 GTK+,那么您已经完成了该列表中的大部分内容。

GObject 方法本身不是成员(有一个 vtable,但它仅用于在首次创建类时在派生类中分配虚拟方法实现)。相反,GObject 中的所有方法都只是普通函数,通常(?)以方法名前缀为前缀,并以指针this作为第一个参数。

例如,C++方法

namespace Lib { // short for Library; to demonstrate GObject's idiomatic naming conventions
    class Foo {
    public:
        void Bar(int z);
    };
}
Run Code Online (Sandbox Code Playgroud)

将是全局命名空间中的一个普通函数,声明为

void lib_foo_bar(LibFoo *foo, int z);
Run Code Online (Sandbox Code Playgroud)

您可以直接调用它,就像任何其他 C 函数一样。

GObject 中的类派生是通过将父类的完整数据结构作为派生类数据结构的第一个成员来进行的。由于与 C 标准中很少讨论的子句有关的各种原因(可能还有 System V ABI 和 gcc、clang 甚至 Microsoft C 编译器的实现),这意味着指向派生类对象的指针等效于指向父类的指针!

因此,如果LibBaz源自LibFoo,您只需说

LibFoo *foobaz = (LibFoo *) baz;
Run Code Online (Sandbox Code Playgroud)

反之亦然:

LibBaz *bazfoo = (LibBaz *) foo;
Run Code Online (Sandbox Code Playgroud)

(后一种方法是 GTK+ 使用的方法GtkWidget;我不知道其他 GObject 库是否也做同样的事情。)

惯用的 GObject 声明包含一堆宏,使类型转换更加简洁,同时添加运行时类型安全检查。我们的LibFoo类将有以下宏:

#define LIB_TYPE_FOO (lib_foo_get_type())
#define LIB_FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), LIB_TYPE_FOO, LibFoo))
Run Code Online (Sandbox Code Playgroud)

有了这个,我们会说

LibFoo *foobaz = LIB_FOO(baz);
LibBaz *bazfoo = LIB_BAZ(foo);
Run Code Online (Sandbox Code Playgroud)

如果 或baz不是foo正确的转换类型,则会将警告记录到标准错误中,您可以使用调试器对其进行中断并进行调查。

lib_foo_get_type()函数(和LIB_TYPE_FOO整洁宏)很重要:它返回一个映射到类型的数字IDLibFoo以供将来参考。如果LibFoo没有这样的映射,它将创建映射,注册类型并创建虚拟方法映射.)

类似的宏允许类型检查:

#define LIB_IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), LIB_TYPE_FOO))
Run Code Online (Sandbox Code Playgroud)

这是一个可以在if语句中使用的简单表达式。

那么调用父类方法呢?好吧,如果我们将以上所有内容放在一起,我们就会得到答案:

lib_foo_parent_method(LIB_FOO(aLibBazInstance), params);
Run Code Online (Sandbox Code Playgroud)

这同样适用于虚拟方法。虚拟方法是使用 GObject 近似 vtable 来实现的,并且对最终程序员是透明的。你要做的就是

lib_foo_virtual_method(LIB_FOO(whatever), params);
Run Code Online (Sandbox Code Playgroud)

(如果您实际上构建派生类本身,则虚拟方法的方式就变得很重要。)

GObject 中没有静态方法,因为方法不像真正的面向对象语言那样与类紧密相关。只需在您的库中创建一个顶级方法:

void lib_something_common(params);
Run Code Online (Sandbox Code Playgroud)

最后,以上所有内容都适用于接口。对于最终用户来说,界面的工作方式完全相同;他们使用相同的

void lib_iface_method(LibIface *iface, params);
Run Code Online (Sandbox Code Playgroud)

方法调用方法、相同的转换规则以及相同的LIB_IFACE()辅助LIB_IS_IFACE()宏。

希望这有帮助!任何进一步的解释都必须解释如何创建 GObject,为了简单起见,我试图将其保留在本答案的范围之外,但无论如何,了解它都是有用的。