Destructing Glib::RefPtr causes failed assertions

2019-04-07 02:07发布

问题:

The guys from Gtkmm are comparing Glib::RefPtr with std::auto_ptr<>:

Glib::RefPtr is a smartpointer. Specifically, it is a reference-counting smartpointer. You might be familiar with std::auto_ptr<>, which is also a smartpointer, but Glib::RefPtr<> is much simpler, and more useful.

But for some strange reason, I can't get my work done with the RefPtr. The same code is just fine with a auto_ptr.

In the following code, SmartPtr is just a placeholder for one of these two smartpointers.

#include <gtkmm.h>
#include <iostream>
#include <tr1/memory>

struct WindowHolder {
  SmartPtr<Gtk::Window> ptr;

  WindowHolder()
    : ptr(new Gtk::Window)
  {
    ptr->signal_delete_event().connect(sigc::mem_fun(*this, &WindowHolder::reset));
    ptr->show_all();
  }

  bool reset(GdkEventAny* event)
  {
    Gtk::Main::quit();
  }
};

int main(int argc, char *argv[])
{
  Gtk::Main kit(argc, argv);
  WindowHolder w;
  kit.run();
}

When compiling, I first define SmartPtr as Glib::RefPtr and then as std::auto_ptr.

$ g++ '-DSmartPtr=Glib::RefPtr' `pkg-config --cflags --libs gtkmm-3.0` main.cc && ./a.out 
(main:22093): GLib-GObject-CRITICAL **: g_object_unref: assertion `G_IS_OBJECT (object)' failed
$ g++ '-DSmartPtr=std::auto_ptr' `pkg-config --cflags --libs gtkmm-3.0` main.cc && ./a.out 
$

The problem is this GLib-GObject-CRITICAL. In my real application, this isn't just a single line but a whole bunch of them. In the second version with std::auto_ptr everything gets destructed well.

Strange enough the code it is just fine in GTK 2:

$ g++ '-DSmartPtr=Glib::RefPtr' `pkg-config --cflags --libs gtkmm-2.4` main.cc && ./a.out 
$

I don't want to depend on std::auto_ptr because it is deprecated and I also don't want to work with a raw pointer because then the destructors have to manually delete the pointers which adds extra complexity ...

My questions are:

  1. Why causes Glib::RefPtr this "critical warning" (probably a double free)?
  2. Why does it work with gtkmm 2.4 but not in 3.0?
  3. Can I fix the code with Glib::RefPtr and gtkmm 3.0?
  4. How should I handle such situations in general?

回答1:

The reference count is too low, and you can fix it by adding a ptr->reference() after ptr->show_all(). I have an explanation, but take it with a grain of salt:

  • Glib::RefPtr doesn't increment the reference count of its object initially.
  • The GtkWindow will have a reference count initially of 1.
  • When your window is closed, the library decrements the reference count of its GtkWindow once.
  • Since the GtkWindow's count is zero, it is destroyed.
  • kit.run() seeing there is no more windows, returns.
  • w goes out of scope and the RefPtr's object's count is decremented causing the error.

I can't really answer #2 or #4 unfortunately, as this area of gtk/gtkmm is still a little mysterious (to me).

Reference: http://www.gtkforums.com/viewtopic.php?t=2412



回答2:

Glib::RefPtr is not meant to be for general use. You should use it when the API forces you to, but not otherwise. GtkWindow (or Gtk::Window) has its own odd memory management which is not really compatible with RefPtr.

If you want a general purpose smartpointer, try std::shared_ptr or std::unique_ptr. Or you could find something in boost.