GObject OOP 语法
GObject OOP Syntax
我正在寻找 GObject 备忘单,了解常见的 OOP 概念如何映射到 GObject 的设施。考虑例如:
AnyGObject *o;
o = anygobject_new();
现在,...的约定是什么
- 调用方法
- 调用基类声明的方法class
- 调用由 class 正在实现的接口声明的方法
- 调用 class 方法
- 调用由基 class
声明的 class 方法
- 调用虚方法
- 投射到基地 class
- 转换为派生的 class
- 转换为 class 正在实现的接口
- 测试对象是否属于特定类型
- ...
说明:
GObject Reference Manual and GObject HowTo 详细解释了如何创建新的 class(class 结构、对象结构、私有结构、各种宏、约定)。将这些设施放在一起可以实现 OOP。然而,似乎没有关于如何始终如一地使用它们的教程。
此答案假设您正在使用 C。其他(通常是面向对象的)语言具有特殊的绑定,使使用 GObject 看起来更自然。
如果您使用过 GTK+,那么您已经完成了该列表中的大部分内容。
GObject 方法本身不是成员(有某种 vtable,但它仅用于在首次创建 class 时在派生 class 中分配虚方法实现)。相反,GObject 中的所有方法都只是普通函数,通常(?)以方法名称前缀为前缀,并以 this
指针作为第一个参数。
例如C++方法
namespace Lib { // short for Library; to demonstrate GObject's idiomatic naming conventions
class Foo {
public:
void Bar(int z);
};
}
将是声明为
的全局命名空间中的普通函数
void lib_foo_bar(LibFoo *foo, int z);
您可以直接调用它,就像任何其他 C 函数一样。
GObject 中的 Class 派生是通过将父 class 的完整数据结构作为派生 class 数据结构的第一个成员来实现的。由于与 C 标准中很少讨论的条款(可能还有 System V ABI 和 gcc、clang 甚至 Microsoft C 编译器的实现)有关的各种原因,这意味着指向派生对象的对象的指针 class 相当于指向父级的指针 class!
所以如果 LibBaz
派生自 LibFoo
,你只需要说
LibFoo *foobaz = (LibFoo *) baz;
反之亦然:
LibBaz *bazfoo = (LibBaz *) foo;
(后一种方法是 GTK+ 用于 GtkWidget
的方法;我不知道其他 GObject 库是否做同样的事情。)
地道的 GObject 声明包括一堆宏,它们使类型转换更加简洁,同时添加运行时类型安全检查。我们的 LibFoo
class 将具有以下宏:
#define LIB_TYPE_FOO (lib_foo_get_type())
#define LIB_FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), LIB_TYPE_FOO, LibFoo))
有了这个,我们会说
LibFoo *foobaz = LIB_FOO(baz);
LibBaz *bazfoo = LIB_BAZ(foo);
如果 baz
或 foo
不是正确的转换类型,警告将记录到标准错误,您可以中断并使用调试器进行调查。
(lib_foo_get_type()
函数(和 LIB_TYPE_FOO
整洁宏)很重要:它 returns 一个数字 ID,映射到 LibFoo
的类型以供将来参考。如果 LibFoo
没有这样的映射,它将创建映射,注册类型并创建虚拟方法映射。)
一个类似的宏允许类型检查:
#define LIB_IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), LIB_TYPE_FOO))
这是一个可以在 if
语句中使用的简单表达式。
那么调用父 class 方法呢?好吧,如果我们将以上所有内容放在一起,我们就有了答案:
lib_foo_parent_method(LIB_FOO(aLibBazInstance), params);
虚拟方法也是如此。虚拟方法是使用 GObject 对 vtables 的近似来实现的,并且对最终程序员是透明的。你要做的就是
lib_foo_virtual_method(LIB_FOO(whatever), params);
(如果您实际构建派生的 class 本身,虚拟方法的 how 就变得很重要。)
GObject 中没有静态方法,因为方法不像在真正的面向对象语言中那样与 class 紧密相关。只需在您的库中创建一个顶级方法:
void lib_something_common(params);
最后,以上所有内容都适用于接口。界面对最终用户的工作方式完全相同;他们使用相同的
void lib_iface_method(LibIface *iface, params);
方法调用方法、相同的转换规则以及相同的 LIB_IFACE()
和 LIB_IS_IFACE()
辅助宏。
希望对您有所帮助!任何进一步的解释都必须解释如何创建 GObject,为简单起见,我试图将其置于此答案的范围之外,但无论如何了解它都是有用的。
我正在寻找 GObject 备忘单,了解常见的 OOP 概念如何映射到 GObject 的设施。考虑例如:
AnyGObject *o;
o = anygobject_new();
现在,...的约定是什么
- 调用方法
- 调用基类声明的方法class
- 调用由 class 正在实现的接口声明的方法
- 调用 class 方法
- 调用由基 class 声明的 class 方法
- 调用虚方法
- 投射到基地 class
- 转换为派生的 class
- 转换为 class 正在实现的接口
- 测试对象是否属于特定类型
- ...
说明: GObject Reference Manual and GObject HowTo 详细解释了如何创建新的 class(class 结构、对象结构、私有结构、各种宏、约定)。将这些设施放在一起可以实现 OOP。然而,似乎没有关于如何始终如一地使用它们的教程。
此答案假设您正在使用 C。其他(通常是面向对象的)语言具有特殊的绑定,使使用 GObject 看起来更自然。
如果您使用过 GTK+,那么您已经完成了该列表中的大部分内容。
GObject 方法本身不是成员(有某种 vtable,但它仅用于在首次创建 class 时在派生 class 中分配虚方法实现)。相反,GObject 中的所有方法都只是普通函数,通常(?)以方法名称前缀为前缀,并以 this
指针作为第一个参数。
例如C++方法
namespace Lib { // short for Library; to demonstrate GObject's idiomatic naming conventions
class Foo {
public:
void Bar(int z);
};
}
将是声明为
的全局命名空间中的普通函数void lib_foo_bar(LibFoo *foo, int z);
您可以直接调用它,就像任何其他 C 函数一样。
GObject 中的Class 派生是通过将父 class 的完整数据结构作为派生 class 数据结构的第一个成员来实现的。由于与 C 标准中很少讨论的条款(可能还有 System V ABI 和 gcc、clang 甚至 Microsoft C 编译器的实现)有关的各种原因,这意味着指向派生对象的对象的指针 class 相当于指向父级的指针 class!
所以如果 LibBaz
派生自 LibFoo
,你只需要说
LibFoo *foobaz = (LibFoo *) baz;
反之亦然:
LibBaz *bazfoo = (LibBaz *) foo;
(后一种方法是 GTK+ 用于 GtkWidget
的方法;我不知道其他 GObject 库是否做同样的事情。)
地道的 GObject 声明包括一堆宏,它们使类型转换更加简洁,同时添加运行时类型安全检查。我们的 LibFoo
class 将具有以下宏:
#define LIB_TYPE_FOO (lib_foo_get_type())
#define LIB_FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), LIB_TYPE_FOO, LibFoo))
有了这个,我们会说
LibFoo *foobaz = LIB_FOO(baz);
LibBaz *bazfoo = LIB_BAZ(foo);
如果 baz
或 foo
不是正确的转换类型,警告将记录到标准错误,您可以中断并使用调试器进行调查。
(lib_foo_get_type()
函数(和 LIB_TYPE_FOO
整洁宏)很重要:它 returns 一个数字 ID,映射到 LibFoo
的类型以供将来参考。如果 LibFoo
没有这样的映射,它将创建映射,注册类型并创建虚拟方法映射。)
一个类似的宏允许类型检查:
#define LIB_IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), LIB_TYPE_FOO))
这是一个可以在 if
语句中使用的简单表达式。
那么调用父 class 方法呢?好吧,如果我们将以上所有内容放在一起,我们就有了答案:
lib_foo_parent_method(LIB_FOO(aLibBazInstance), params);
虚拟方法也是如此。虚拟方法是使用 GObject 对 vtables 的近似来实现的,并且对最终程序员是透明的。你要做的就是
lib_foo_virtual_method(LIB_FOO(whatever), params);
(如果您实际构建派生的 class 本身,虚拟方法的 how 就变得很重要。)
GObject 中没有静态方法,因为方法不像在真正的面向对象语言中那样与 class 紧密相关。只需在您的库中创建一个顶级方法:
void lib_something_common(params);
最后,以上所有内容都适用于接口。界面对最终用户的工作方式完全相同;他们使用相同的
void lib_iface_method(LibIface *iface, params);
方法调用方法、相同的转换规则以及相同的 LIB_IFACE()
和 LIB_IS_IFACE()
辅助宏。
希望对您有所帮助!任何进一步的解释都必须解释如何创建 GObject,为简单起见,我试图将其置于此答案的范围之外,但无论如何了解它都是有用的。