[英]How do I close this Gtk::MessageDialog before it's parent window is destructed?
我目前正在尝试创建一个简单的 Gtkmm 程序,该程序具有一个生成对话框的按钮。 但是,我目前遇到问题,因为 AppWindow 类的析构函数导致关闭对话框的段错误。 我在调用close之前检查unique_ptr
是否为nullptr
,但即使进行了检查,如果对话框在主窗口之前已经关闭,它也会崩溃。 我在这里采取正确的方法吗? 在调用析构函数之前是否释放了unique_ptr
?
主程序
#include "AppWindow.hpp"
#include <cstdio>
#include <cstdlib>
#include <gtkmm/application.h>
int main(int argc, char **argv) {
std::shared_ptr<Gtk::Application> app = Gtk::Application::create("org.dylanweber.test");
return app->make_window_and_run<AppWindow>(argc, argv);
}
应用程序窗口.hpp
#include <gtkmm/button.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/window.h>
#include <iostream>
#pragma once
class AppWindow : public Gtk::Window {
public:
AppWindow();
virtual ~AppWindow();
private:
Gtk::Button m_button;
std::unique_ptr<Gtk::MessageDialog> dialog;
void on_button_clicked();
};
应用程序窗口.cpp
#include "AppWindow.hpp"
AppWindow::AppWindow() : m_button("Hello, world!") {
this->m_button.set_margin(10);
this->m_button.signal_clicked().connect(sigc::mem_fun(*this, &AppWindow::on_button_clicked));
this->set_child(this->m_button);
}
AppWindow::~AppWindow() {
if (this->dialog != nullptr) {
this->dialog->close(); // seg fault here
}
}
void AppWindow::on_button_clicked() {
this->dialog = std::make_unique<Gtk::MessageDialog>(
"Button clicked", false, Gtk::MessageType::QUESTION, Gtk::ButtonsType::OK);
this->dialog->set_transient_for(*this);
this->dialog->set_secondary_text("Hello");
this->dialog->set_default_response(Gtk::ResponseType::OK);
this->dialog->show();
}
评论中的建议是正确的,您应该将析构函数留空,而不是在那里调用dialog->close()
。 要了解原因,请注意此函数是 GTK C API 函数gtk_window_close
的包装器,为Gtk::Window class
定义如下( Gtk::MessageDialog
通过Gtk::Dialog
继承):
// in gtk/gtkmm/window.cc
void Window::close()
{
gtk_window_close(gobj());
}
这里gobj()
返回指向 C API 的 GTK 句柄的内部gobject_
指针(在ObjectBase
类中定义),当前类封装了该句柄。 这个指针在gtk_window_close
这种方式使用:
// in gtk/gtkwindow.c
void
gtk_window_close (GtkWindow *window)
{
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
if (!_gtk_widget_get_realized (GTK_WIDGET (window)))
return;
if (priv->in_emit_close_request)
return;
g_object_ref (window);
if (!gtk_window_emit_close_request (window))
gtk_window_destroy (window);
g_object_unref (window);
}
// in gtk/gtkwidgetprivate.h
static inline gboolean
_gtk_widget_get_realized (GtkWidget *widget)
{
return widget->priv->realized;
}
正如 VS 调试器在运行您的示例时所说的那样,发生段错误是因为_gtk_widget_get_realized
中的widget
为 0。 它在对话框关闭时被清除,因为默认情况下关闭(通过close
方法或关闭按钮)一个窗口意味着销毁它,例如通过调用gtk_window_destroy
中的gtk_window_close
可以看到。 使用 GTK 的 C API gtk_window_destroy
函数通过一个相当复杂的回调机制进行销毁,达到了与内存管理相关的适当注册的 C++ 函数。 数据断点有助于准确定位gobject_
设置为 0 的位置 - Object::destroy_notify_
(它本身是从 GTK 的引用计数代码中间接调用的,因为对话框的引用计数降至 0):
// gtk/gtkmm/object.cc
void Object::destroy_notify_()
{
//Overriden.
//GTKMM_LIFECYCLE
#ifdef GLIBMM_DEBUG_REFCOUNTING
g_warning("Gtk::Object::destroy_notify_: this=%p, gobject_=%p\n", (void*)(Glib::ObjectBase*)this, (void*)gobject_);
if(gobject_)
g_warning(" gtypename=%s\n", G_OBJECT_TYPE_NAME(gobject_));
#endif
//Actually this function is called when the GObject is finalized, not when it's
//disposed. Clear the pointer to the GObject, because otherwise it would
//become a dangling pointer, pointing to a non-existent object.
gobject_ = nullptr;
if(!cpp_destruction_in_progress_) //This function might have been called as a side-effect of destroy_() when it called g_object_run_dispose().
{
if (!referenced_) //If it's manage()ed.
{
#ifdef GLIBMM_DEBUG_REFCOUNTING
g_warning("Gtk::Object::destroy_notify_: before delete this.\n");
#endif
delete this; //Free the C++ instance.
}
else //It's not managed, but the C gobject_ just died before the C++ instance..
{
#ifdef GLIBMM_DEBUG_REFCOUNTING
g_warning("Gtk::Object::destroy_notify_: setting gobject_ to 0\n");
#endif
}
}
}
因此,在关闭对话框后, MessageDialog
的内部句柄被清除为 0(顺便说一句,在此期间未调用 MessageDialog 析构函数,我也在调试器中进行了检查),并且由于您调用了一个假定它不是的close
函数,你会遇到内存访问问题。 析构函数在gobject_
已被清除的情况下正确工作,因此在关闭后在on_button_clicked
中重新创建MessageDialog
不会出现问题(如果存在, std::unique_ptr
为先前的对象调用析构函数),以及在gobject_
仍然指向的情况下到有效的对象,所以即使在重新创建之前没有关闭对话框,你也不应该有问题。 出于同样的原因,在AppWindow
析构函数中调用MessageDialog
析构函数是可以的,无论对话框是否在窗口之前关闭。
PS 请注意,此处的 gtkmm .cc
文件来自 vcpkg buildtrees,在构建过程中使用 gtkmm 的自定义预处理gmmproc
工具从.ccg
源文件生成,因此您不会在存储库中找到它们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.