简体   繁体   English

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

[英]Gtkmm add/Remove widget leaks Why?

When I add a widget to container, then I remove it.当我将小部件添加到容器时,我将其删除。 Widget is leaked, why?微件泄露了,为什么? I used a "MyWidget" to spy widget deletion but I get same result from a classic Gtk::Label.我使用“MyWidget”来监视小部件删除,但我从经典的 Gtk::Label 得到了相同的结果。 Code below have been tested on two distro.下面的代码已经在两个发行版上进行了测试。

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

I expected to see ~MyWidget destructor executed.我希望看到 ~MyWidget 析构函数被执行。

This is my glade file content这是我的空地文件内容

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

The incorrect assumption we made here is that Gtk::Window::remove() destroys the widget it removes, but in reality, it only removes its reference from the parent container.我们在这里做出的错误假设是Gtk::Window::remove()破坏了它移除的小部件,但实际上,它只是从父容器中移除了它的引用。 I instrumented your code with extra cout s to see what was happening and when this line is executed:我用额外的cout检测了您的代码,以查看发生了什么以及何时执行此行:

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

A MyWidget instance is created by the builder. MyWidget实例由构建器创建。 At this point, the builder owns a reference to it and the ref count is one.此时,构建器拥有对它的引用并且引用计数为 1。 Then, when the following line is executed:然后,当执行以下行时:

window.add(*widget);

the window, which is a one element container as you pointed out, takes a (owning) reference on the MyWidget instance.正如您所指出的,window 是一个单元素容器, MyWidget实例进行(拥有)引用。 So the reference count on the MyWidget instance is now 2: one for the builder, one for the window. This is where it gets tricky.所以MyWidget实例上的引用计数现在是 2:一个用于构建器,一个用于 window。这就是它变得棘手的地方。 When executing:执行时:

window.remove();

the window "removes" its reference to the MyWidget instance, in the sense that it will no longer show it, but the reference count on it is not decremented. window“删除”了它对MyWidget实例的引用,从某种意义上说,它将不再show它,但它的引用计数不会减少。 At this point, there are still two references on the MyWidget instance: one for the builder, and one that is not owned by anyone (it is leaked, unless someone explicitly calls delete on it).此时, MyWidget实例上仍然有两个引用:一个用于构建器,另一个不属于任何人(它被泄露,除非有人明确地对其调用delete )。 When this line is executed:执行此行时:

builder.reset();

The builder is destroyed, and its reference to the MyWidget instance is removed, bringing the reference count on it to one.构建器被销毁,它对MyWidget实例的引用被删除,使它的引用计数变为 1。 This is why printing the reference count always prints "1" in your case.这就是为什么在您的案例中打印引用计数总是打印“1”的原因。

Now if we comment out:现在,如果我们注释掉:

window.remove();

What happens it that at the end of main , the window destroys itself and the widget it references (if any).main结束时会发生什么, window 销毁自身及其引用的小部件(如果有)。 In your case, this is the MyWidget instance, so the destructor is called and the reference count is brought to 0. You don't see it printed because it happens after the cout call, at the } (end of scope for main ).在您的情况下,这是MyWidget实例,因此调用析构函数并将引用计数设为 0。您看不到它被打印出来,因为它发生在cout调用之后,在} (scope 的末尾,对于main )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM