构造 GTK 组合框时从不兼容的指针类型初始化

Initialization from incompatible pointer type when constructing a GTK combobox

GtkComboBoxText  *main_combo=gtk_combo_box_text_new();

以上代码产生以下错误:

source.c:31:32: warning: initialization from incompatible pointer type [enabled by default] GtkComboBoxText *main_combo=gtk_combo_box_text_new();

我不确定我是否理解错误的含义或如何修复它。

简答:

像这样声明组合框:

GtkWidget  *main_combo=gtk_combo_box_text_new();

并像这样使用它:

gtk_combo_box_text_insert(GTK_COMBO_BOX_TEXT(main_combo), 0, NULL, "foobar");

长答案:

GTK 小部件遵循面向对象的方法,其中特定小部件类型从更通用的小部件类型继承属性和方法。

例如,根据文档,GtkComboBoxText 具有以下层次结构:

GObject
\-- GInitiallyUnowned
    \-- GtkWidget
        \-- GtkContainer
            \-- GtkBin
                \-- GtkComboBox
                    \-- GtkComboBoxText

另外,widget类型也可以实现接口。例如,从文档中,

GtkComboBoxText implements AtkImplementorIface, GtkBuildable, GtkCellLayout and GtkCellEditable.

这意味着,给定一个指向 GtkComboBoxText 类型对象的指针 ptr,我可以:

  1. 在 GtkComboBoxText 的方法上使用它,例如

    gtk_combo_box_text_insert(ptr, 0, NULL, "foobar");
    
  2. 在它继承自的任何类型的方法上使用它,例如

    gtk_widget_destroy(ptr);
    

    GtkWidget *wid = gtk_bin_get_child(ptr);
    
  3. 在它实现的任何接口的方法上使用它,例如

    gtk_cell_editable_start_editing(ptr, event);
    

因为 C 不是面向对象的语言,当您编译程序时,编译器不知道 GtkComboBoxText 继承自 GtkWidget。这意味着如果您有一个声明为 GtkComboBoxText 类型的对象,当您在 GtkWidget 的方法中使用它时,编译器会看到一个不兼容的指针,反之亦然。

现在 GTK 的作者有两个明智的对象构造函数选择。例如,对于 gtk_combo_box_text(),他们可能会选择 return GtkComboBoxText *,或者他们可能会选择层次结构顶部的有用类型 (GtkWidget *) 来 return。他们选择了后者。

所以您实际上有几个选项可以修复警告消息:

  1. 当您需要将它用于继承类型的方法时转换您的小部件:

    GtkWidget *w = gtk_combo_box_text_new();
    gtk_combo_box_text_insert(GTK_COMBO_BOX_TEXT(w), 0, NULL, "foobar");
    gtk_combo_box_set_active(GTK_COMBO_BOX(w), -1);
    gtk_buildable_set_name(GTK_BUILDABLE(w), "name");
    gtk_widget_destroy(w); // no cast needed here
    
  2. 初始化时投射您的小部件:

    GtkComboBoxText *w = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
    gtk_combo_box_text_insert(w, 0, NULL, "foobar"); // no cast needed here
    gtk_combo_box_set_active(GTK_COMBO_BOX(w), -1);
    gtk_buildable_set_name(GTK_BUILDABLE(w), "name");
    gtk_widget_destroy(GTK_WIDGET(w));
    
  3. 使用void *:

    void *w = gtk_combo_box_text_new();
    gtk_combo_box_text_insert(w, 0, NULL, "foobar");
    gtk_combo_box_set_active(w, -1);
    gtk_buildable_set_name(w, "name");
    gtk_widget_destroy(w);
    

虽然第三种方法看起来更整洁,但我个人不会推荐它,因为它可能无法检测到一些可能会犯的错误。如果您非常频繁地调用 gtk_combo_box_text_*() 函数并且很少使用其他函数,则第二种方法可能有其优势。然而,我通常使用第一种方法,因为它的用法更加清晰和直接(即在使用 gtk_widget_*() 函数时不要转换,否则转换)。第一种方法也是文档和gtk3-demo中示例中使用的方法。

旁注:

也可以使用传统的 C 风格转换,例如

gtk_combo_box_text_insert((GtkComboBoxText *)main_combo, 0, NULL, "foobar");

但是,宏 GTK_COMBO_BOX_TEXT() 提供了一些错误检查功能,因此如果您不小心传递了不是 GtkComboBoxText 的内容,宏将导致显示错误消息,而 C 风格的转换可能不会。