【问题标题】:GObject and inheritanceGObject 和继承
【发布时间】:2018-01-17 16:48:32
【问题描述】:

我在 Glib 上做一些严肃的软件。我意识到有些话题我并不真正理解。 IRC 也没有帮助...

当我们进行继承时,我们可以有两个类。第一个A直接继承自GObject,B继承自A。然后我来了:

https://developer.gnome.org/gobject/stable/chapter-gobject.html

static void
viewer_file_constructed (GObject *obj)
{
  /* update the object state depending on constructor properties */

  /* Always chain up to the parent constructed function to complete object
   * initialisation. */
  G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj);
}

static void
viewer_file_class_init (ViewerFileClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->constructed = viewer_file_constructed;
}

但是当你有这种安排时。子类这样做:object_class->constructed = viewer_file_constructed;在 B 中重写实际上是构造的唯一内存地址。所以这意味着 G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj);将递归调用 B->constructed。这不是我们想要的。

也许我不明白,但我想B中的内存结构是这样的:

struct _B
{
  A parent_instance;

  /* instance members */
};

内部表示应该是这样的:

[   Gobject struct memory ]
[                         ]
[   Gobject variables     ]
[   A struct memory       ]
[   A variables           ] 
[                         ]
[   B struct memory       ]
[   B variables           ] 

所以在 B 中转换时的 GObject 内存是由 B 和 A 分类共享的。并且所有地址都在同一个 i...

  • G_OBJECT_CLASS (B)->构造
  • G_OBJECT_CLASS (A)->已构建

这是正确的吗? 所以如果我想覆盖构造......我是否必须保存之前的指针,然后用我的 init 覆盖它?所以我可以在处理完后调用原始的?

这同样适用于属性。因为 A 通过枚举来定义它的属性,所以可以从 0 到 N。

所以我认为 B 属性应该从 N 开始,而不是 0。否则 A 的属性由 B 处理,并且可能具有不同的数据结构和名称。

检查这个: https://developer.gnome.org/gobject/stable/gobject-properties.html

enum
{
  PROP_FILENAME = 1,
  PROP_ZOOM_LEVEL,
  N_PROPERTIES
};

如果两个类都定义了索引为 1 的属性。那会有问题,因为 glib 不知道应该处理谁。我想子类 B 会处理它但不正确,因为可能 B 正在等待,比如 PROP_DIRECTORY,但因为索引是相同的。 glib 能否发送到正确的实例?

只有在寄存器 glib 会根据层次结构级别添加一些偏移量时,它才会起作用。有人可以解释一下这是如何工作的吗?我找不到任何具有所需技术细节的文档。

【问题讨论】:

  • 可能你得到答案的唯一方法就是查看源代码......
  • GLib 已经存在 20 多年了。它的官方文档和它一样,是稳定的,并且变化相对缓慢。你应该假设这些官方文档知道他们在说什么,而不是认为你是第一个突然发现这种根本性的、完全破坏性错误的人......在这种情况下,你应该假设你误解了,需要阅读更多,并不是说文档是错误的。不是。

标签: inheritance glib gobject


【解决方案1】:

内部表示应该是这样的:

不完全是。 GObjectGObjectClass 结构之间存在差异。每个对象实例有一个GObject struct 实例,但整个类只有一个GObjectClass 实例。

如果您有一个派生自GObject 的类FooBar,则FooBarClass 结构将类似于:

typedef struct
{
  GObjectClass parent_class;

  /* Virtual methods for FooBar instances: */
  void (*vfunc) (FooBar *self);
} FooBarClass;

堆上会有一个FooBarClass 的实例。由于它包含整个GObjectClass 结构作为其parent_class 成员,这意味着它有自己的finalizedisposeget_property 等虚拟方法指针。

在堆中,有一个 GObject 类型的 GObjectClass 实例。它包含另一组finalizedispose等虚方法指针。

由于FooBar 派生自GObjectfoo_bar_parent_class 将被设置为指向GObjectClass 实例。这就是允许链接的原因。

所以如果你想实现constructed 虚方法并链接起来(你必须链接constructed),就像example code in the documentation you linked to does 那样做。没错。


这同样适用于属性。因为 A 定义了它 枚举的属性,可以从 0 到 N。

不正确。当使用g_object_class_install_properties() 向类注册属性时,属性索引与该类的类结构内的GObjectClass 实例相关联。它们与 GObject 类型的独立 GObjectClass 结构无关。这和上面的原理是一样的。

换句话说,没有属性索引的全局注册表:所有这些都是按类完成的。因此,您可以(并且应该)为每个类从 1 开始索引属性。它们不会冲突。

请注意,正如g_object_class_install_properties() 的文档中所述,属性索引 0 是特殊的,不得使用。您必须从 1 开始您的属性索引。


正如 ptomato 所说,这远远超出了适合文档的详细程度。你应该阅读源代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-07
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 2013-12-12
    • 2020-06-19
    相关资源
    最近更新 更多