Разрушение Glib::RefPtr приводит к ошибочным утверждениям в ядре GTK 3.

Ребята из Gtkmm сравнивают Glib::RefPtr с std::auto_ptr<>:

Glib::RefPtr — это умный указатель. В частности, это смарт-пойнтер с подсчетом ссылок. Возможно, вы знакомы с std::auto_ptr<>, который также является умным указателем, но Glib::RefPtr<> намного проще и полезнее.

Но по какой-то странной причине я не могу работать с RefPtr. Тот же код прекрасно работает с auto_ptr.

В следующем коде SmartPtr — это просто заполнитель для одного из этих двух умных указателей.

#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();
}

При компиляции я сначала определяю SmartPtr как Glib::RefPtr, а затем как 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 
$

Проблема в том, что GLib-GObject-CRITICAL. В моем реальном приложении это не просто одна строка, а целая их куча. Во второй версии с std::auto_ptr все хорошо деструктируется.

Как ни странно, код отлично работает в GTK 2:

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

Я не хочу зависеть от std::auto_ptr, потому что он устарел, и я также не хочу работать с необработанным указателем, потому что тогда деструкторы должны вручную удалять указатели, что добавляет дополнительную сложность...

Мои вопросы:

  1. Почему вызывает Glib::RefPtr это "критическое предупреждение" (вероятно, двойное бесплатное)?
  2. Почему это работает с gtkmm 2.4, но не работает с 3.0?
  3. Можно ли исправить код с Glib::RefPtr и gtkmm 3.0?
  4. Как мне вообще вести себя в таких ситуациях?

person glitto    schedule 14.04.2012    source источник


Ответы (2)


Счетчик ссылок слишком мал, и вы можете исправить это, добавив ptr->reference() после ptr->show_all(). У меня есть объяснение, но отнеситесь к нему с недоверием:

  • Glib::RefPtr изначально не увеличивает счетчик ссылок на свой объект.
  • GtkWindow изначально будет иметь счетчик ссылок, равный 1.
  • Когда ваше окно закрывается, библиотека один раз уменьшает счетчик ссылок своего GtkWindow.
  • Поскольку счетчик GtkWindow равен нулю, он уничтожается.
  • kit.run(), видя, что окон больше нет, возвращается.
  • w выходит за рамки, и счетчик объекта RefPtr уменьшается, вызывая ошибку.

К сожалению, я не могу ответить № 2 или № 4, так как эта область gtk/gtkmm все еще немного загадочна (для меня).

Ссылка: http://www.gtkforums.com/viewtopic.php?t=2412

person ergosys    schedule 14.04.2012

Glib::RefPtr не предназначен для общего использования. Вы должны использовать его, когда API вынуждает вас к этому, но не иначе. GtkWindow (или Gtk::Window) имеет собственное странное управление памятью, которое на самом деле несовместимо с RefPtr.

Если вам нужен умный указатель общего назначения, попробуйте std::shared_ptr или std::unique_ptr. Или вы можете найти что-то в бусте.

person murrayc    schedule 17.04.2012
comment
Разве Glib::RefPtr не использует внутренний счетчик ссылок GObject? - person el.pescado; 08.07.2016
comment
@el.pescado Проверьте документацию: RefPtr‹› может хранить любой класс, который имеет методы reference() и unference(), а деструктор которого имеет значение noexcept (по умолчанию для деструкторов). В gtkmm это все, что происходит от Glib::ObjectBase, например Gdk::Pixmap. Итак, да. Это навязчивый интеллектуальный указатель, предназначенный только для обернутых GObject, использующий их уже существующий подсчет ссылок. Вместо того, чтобы оборачивать в него собственные GObject, у меня сложилось впечатление, что это всего лишь деталь реализации библиотек mm. - person underscore_d; 13.08.2016