当我向容器添加一个小部件时,然后将其删除。插件泄露,为什么?我使用了"我的小部件"间谍小部件删除,但我从一个经典的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
的作用域结束)。