Gtkmm添加/删除插件泄漏为什么?



当我向容器添加一个小部件时,然后将其删除。插件泄露,为什么?我使用了"我的小部件"间谍小部件删除,但我从一个经典的Gtk::标签得到相同的结果。下面的代码已经在两个发行版上测试过了。

#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>

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

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

MyWidget实例由builder创建。此时,构建器拥有对它的引用,并且refcount为1。然后,当执行以下行时:

window.add(*widget);

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

window.remove();

窗口"移除"它对MyWidget实例的引用,从某种意义上说,它将不再show它,但它的引用计数没有减少。此时,在MyWidget实例上仍然有两个引用:一个用于构建器,另一个不属于任何人(它是泄漏的,除非有人显式地在其上调用delete)。当执行这一行时:

builder.reset();

构造器被销毁,它对MyWidget实例的引用被删除,使它的引用计数为1。这就是为什么打印引用计数总是打印&;1&;;在你的情况下。

现在如果我们注释掉:

window.remove();

如果在main结束时,窗口销毁了自己和它引用的小部件(如果有的话),会发生什么?在您的示例中,这是MyWidget实例,因此将调用析构函数并使引用计数变为0。您不会看到它被打印出来,因为它发生在cout调用之后,在}(main的作用域结束)。

最新更新