c++ Gtkmm添加/删除小部件泄漏为什么?

ny6fqffe  于 2022-11-19  发布在  其他
关注(0)|答案(1)|浏览(178)

当我添加一个小部件到容器,然后我删除它。小部件泄漏,为什么?我用“MyWidget”间谍小部件删除,但我得到了同样的结果从一个经典的Gtk::Label。下面的代码已经在两个发行版测试。

#include <iostream>
// gtkmm30-3.24.5-1.el9.x86_64
// or gtkmm3 3.24.7-1 (arch)
#include <gtkmm/main.h>
#include <gtkmm/builder.h>
#include <gtkmm/label.h>
#include <gtkmm/window.h>
#include <gtkmm/box.h>

#include <cassert>

using namespace std;

class MyWidget : public Gtk::Label{
public:
    MyWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder);
    virtual ~MyWidget();
};

MyWidget::~MyWidget(){
    cout << "MyWidget::~MyWidget()" << endl;
}

MyWidget::MyWidget(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
 : Gtk::Label(cobject)
{
    assert(builder);
}

int main()
{
    Gtk::Main main;
    
    // Create widget
    auto builder = Gtk::Builder::create_from_file("widget.glade");
    MyWidget* widget = nullptr;
    builder->get_widget_derived("widget", widget);

    {
        Gtk::Window window;
        window.add(*widget);
        // Use window ....
        window.remove(); // No leak if this line is commented
    }
    
    builder.reset();
    cout << G_OBJECT(widget->gobj())->ref_count << endl; // Print => 1
    // Expected to see "MyWidget::~MyWidget()" but No ! Why ?
}

我希望看到~MyWidget析构函数被执行。
这是我的glade文件内容

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.40.0 -->
<interface>
  <requires lib="gtk+" version="3.24"/>
  <object class="GtkLabel" id="widget">
    <property name="visible">True</property>
    <property name="can-focus">False</property>
    <property name="label" translatable="yes">widget</property>
  </object>
</interface>
yeotifhr

yeotifhr1#

我们在这里做了一个错误的假设,即Gtk::Window::remove()破坏了它所删除的小部件,但实际上,它只是删除了它对父容器的引用。我用额外的cout来检测代码,以查看发生了什么以及何时执行了这一行:

builder->get_widget_derived("widget", widget);

生成器创建了一个MyWidget示例。此时,生成器拥有对它的引用,引用计数为1。然后,当执行以下代码行时:

window.add(*widget);

这个窗口是一个元素容器,正如您所指出的,它在MyWidget示例上有一个引用,所以MyWidget示例上的引用计数现在是2:一个用于构建器,一个用于窗口。这就是它变得棘手的地方。当执行时:

window.remove();

该窗口“移除”其对MyWidget示例的引用,意思是它将不再对show示例进行show,但其上的引用计数不递减。此时,在MyWidget示例上仍存在两个引用:一个用于构建器,另一个不为任何人所有(除非有人显式地对它调用delete,否则它是泄漏的)。

builder.reset();

构建器被销毁,它对MyWidget示例的引用被删除,使其引用计数为1。这就是为什么在您的情况下打印引用计数总是打印“1”。
现在如果我们注解出:

window.remove();

如果在main结束时,窗口销毁自身及其引用的小部件,会发生什么情况(如果有的话)。在您的例子中,这是MyWidget示例,因此析构函数被调用,引用计数被置为0。您看不到它,因为它发生在cout调用之后,在}main的作用域结尾)。

相关问题