[英]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.