AnyGObject *o;
o = anygobject_new();
现在,关于以下内容的惯例是什么:
- 调用方法
- 调用由基类声明的方法
- 调用由接口实现的方法
- 调用类方法
- 调用由基类声明的类方法
- 调用虚拟方法
- 转换为基类
- 转换为派生类
- 转换为实现该类的接口
- 测试对象是否属于特定类型
- ...
AnyGObject *o;
o = anygobject_new();
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中的类派生是通过将父类的完整数据结构作为派生类的数据结构的第一个成员来实现的。由于涉及到C标准中很少讨论的条款(可能还包括System V ABI和gcc、clang甚至Microsoft C编译器的实现),这意味着指向派生类对象的指针等同于指向父类的指针!
因此,如果LibBaz
派生自LibFoo
,那么你只需要说:
LibFoo *foobaz = (LibFoo *) baz;
LibBaz *bazfoo = (LibBaz *) foo;
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))
因此,我们应该这样说:
LibFoo *foobaz = LIB_FOO(baz);
LibBaz *bazfoo = LIB_BAZ(foo);
如果baz
或者foo
不是正确的类型,警告将被记录到标准错误中,您可以在断点处进行调试。
(lib_foo_get_type()
函数(和LIB_TYPE_FOO
宏)非常重要:它返回一个数字ID,该ID映射到LibFoo
的类型,以供将来参考。如果LibFoo
没有这样的映射,它将创建该映射,注册类型并创建虚拟方法映射。)
类似的宏允许进行类型检查:
#define LIB_IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), LIB_TYPE_FOO))
这是一个简单的表达式,可以在if
语句中使用。
那么如何调用父类方法呢?如果我们将上面所有内容综合起来,就有了答案:
lib_foo_parent_method(LIB_FOO(aLibBazInstance), params);
lib_foo_virtual_method(LIB_FOO(whatever), params);
如果您实际构建了派生类本身,虚拟方法的“如何”变得很重要。
GObject 中没有静态方法,因为方法与真正的面向对象语言中的类没有那么紧密地联系在一起。只需在您的库中创建一个顶层方法:
void lib_something_common(params);
最后,以上所有内容同样适用于接口。对于最终用户而言,接口的使用方式完全相同;它们使用相同的
void lib_iface_method(LibIface *iface, params);
在方法调用方面,使用相同的转换规则以及相同的LIB_IFACE()
和LIB_IS_IFACE()
辅助宏。
希望这有所帮助!任何进一步的解释都需要涉及如何创建GObject,为了简单起见,我尝试将其保持在本答案范围之外,但这也是一个有用的知识。