GObject-OOP语法
我正在寻找一份GObject的备忘单,看看常见的OOP概念如何映射到GObject的设施。例如,考虑:GObject-OOP语法,c,oop,gtk,gobject,C,Oop,Gtk,Gobject,我正在寻找一份GObject的备忘单,看看常见的OOP概念如何映射到GObject的设施。例如,考虑: AnyGObject *o; o = anygobject_new(); 现在,这些惯例是为了什么 调用方法 调用基类声明的方法 调用由类正在实现的接口声明的方法 调用类方法 调用基类声明的类方法 调用虚拟方法 铸造到基类 转换到派生类 强制转换到类正在实现的接口 测试对象是否属于特定类型 澄清: 并详细说明如何创建新类(类结构、对象结构、私有结构、各种宏、约定)。这些功能结合在一起可
AnyGObject *o;
o = anygobject_new();
现在,这些惯例是为了什么
- 调用方法
- 调用基类声明的方法
- 调用由类正在实现的接口声明的方法
- 调用类方法
- 调用基类声明的类方法
- 调用虚拟方法
- 铸造到基类
- 转换到派生类
- 强制转换到类正在实现的接口
- 测试对象是否属于特定类型
并详细说明如何创建新类(类结构、对象结构、私有结构、各种宏、约定)。这些功能结合在一起可以实现OOP。然而,似乎没有关于如何一致地使用它们的教程。这个答案假设您正在使用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);
};
}
将是声明为的全局命名空间中的普通函数
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\u foo\u get\u type()
很重要:它返回一个映射到LibFoo
类型的数字ID,以供将来参考。如果LibFoo
没有这样的映射,它将创建映射,注册类型并创建虚拟方法映射。)
类似的宏允许类型检查:
#define LIB_IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), LIB_TYPE_FOO))
这是一个简单的表达式,可以在if
语句中使用
那么调用父类方法呢?好吧,如果我们把以上所有因素放在一起,我们会得到答案:
lib_foo_parent_method(LIB_FOO(aLibBazInstance), params);
虚拟方法也是如此。虚拟方法是使用vtables的GObject近似实现的,并且对最终程序员是透明的。你要做的就是
lib_foo_virtual_method(LIB_FOO(whatever), params);
(如果您实际构建派生类本身,那么虚拟方法的使用方式就变得非常重要。)
GObject中没有静态方法,因为方法与类的联系不像在真正的面向对象语言中那样紧密。只需在库中创建顶级方法:
void lib_something_common(params);
最后,以上所有内容都适用于接口。接口的工作方式与最终用户完全相同;他们使用同样的方法
void lib_iface_method(LibIface *iface, params);
方法调用的方法、相同的强制转换规则以及相同的LIB\u IFACE()
和LIB\u IS\u IFACE()
helper宏
希望这有帮助!任何进一步的解释都必须解释如何创建一个GObject,为了简单起见,我试图将其排除在这个答案的范围之外,但无论如何,这是一件有用的事情。并不是所有这些概念都能很好地映射到glib。它实际上并不是一个完整的OOP工具,而是一个类型系统,具有一些类似对象的特性。没有你所期望的真正的继承——只有COM风格的界面外观。按照约定,“方法”只是将目标对象作为第一个参数的结构/类的函数成员,没有特殊的语法或糖来调用它们。object->obj_方法(obj,…);不过还有RTTI,我不认为你会找到很多关于这个问题的文档,但我希望我会被反驳:)无论如何,这个问题迟早会变成一个维基。谢谢你的详细解释。我希望这篇文章能以某种方式进入GObject手册。我向bugzilla提交了一份增强请求: