英文:
Why can I not declare a top-level GTKMM objects?
问题
我对GTK+ (C)很熟悉,但对GTKMM还很新,我从一些示例代码中适应了一个非常简单的示例应用程序。
#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/application.h>
#include <iostream>
int main (int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
Gtk::Window win;
Gtk::Button m_button("Hello World");
m_button.signal_clicked().connect([](){
std::cout << "Hello World" << std::endl;
});
win.set_border_width(10);
win.add(m_button);
m_button.show();
return app->run(win);
}
这按预期工作,创建一个带有<kbd>Hello World</kbd>按钮的窗口,当点击时打印Hello World
。
Hello World
Hello World
Hello World
我不太确定这在C++中是如何分配内存的,但我认为使用静态内存而不是堆内存可能更好,这可以通过将一些声明移到顶层来实现,或者如果我只想在以后从不同的函数中访问它们:
#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/application.h>
#include <iostream>
Gtk::Window win;
Gtk::Button m_button("Hello World");
int main (int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
m_button.signal_clicked().connect([](){
std::cout << "Hello World" << std::endl;
});
win.set_border_width(10);
win.add(m_button);
m_button.show();
return app->run(win);
}
这不按预期工作。
Segmentation fault (core dumped)
为什么GTKMM和/或C++不喜欢*'全局'*/顶层变量?有人能解释一下背后发生了什么,或者我如何拥有一个顶层对象而不出现此错误?
英文:
I am old to GTK+ (C) but new to GTKMM and adapted a very simple example app from some example code.
#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/application.h>
#include <iostream>
int main (int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
Gtk::Window win;
Gtk::Button m_button("Hello World");
m_button.signal_clicked().connect([](){
std::cout << "Hello World" << std::endl;
});
win.set_border_width(10);
win.add(m_button);
m_button.show();
return app->run(win);
}
This works as expected, creating a window with a <kbd>Hello World</kbd> button that prints Hello World
when clicked.
>```
>Hello World
>Hello World
>Hello World
I am not entirely sure how this memory is allocated in C++, but I thought using static memory as opposed to heap memory would be better, and this could potentially be achieved by moving some of the declarations to the top level. Or if I just want to access these later on from separate functions:
#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/application.h>
#include <iostream>
Gtk::Window win;
Gtk::Button m_button("Hello World");
int main (int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
m_button.signal_clicked().connect([](){
std::cout << "Hello World" << std::endl;
});
win.set_border_width(10);
win.add(m_button);
m_button.show();
return app->run(win);
}
That does not work as expected.
>```none
>Segmentation fault (core dumped)
Why does GTKMM and/or C++ not like the 'global'/top-level variables? Can someone explain what is going on behind the scenes, or how I could have a top-level object without this error?
答案1
得分: 1
段错误的原因正如评论中@Retired Ninja提到的那样。在工具包通过调用Gtk::Application::create
初始化之前,不应该尝试实例化小部件。话虽如此,你可以使用“顶级”变量。
解决方案1(堆)
你可以使用std::unique_ptr
(专门设计用于拥有单个资源,如小部件)来存储你的“顶级”变量。
例如:
#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/application.h>
#include <iostream>
std::unique_ptr<Gtk::Window> win;
std::unique_ptr<Gtk::Button> m_button;
int main (int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
win = std::make_unique<Gtk::Window>();
m_button = std::make_unique<Gtk::Button>("Hello World");
m_button->signal_clicked().connect([](){
std::cout << "Hello World" << std::endl;
});
win->set_border_width(10);
win->add(*m_button);
m_button->show();
return app->run(*win);
}
这些行:
std::unique_ptr<Gtk::Window> win;
std::unique_ptr<Gtk::Button> m_button;
实例化了指向窗口和按钮的唯一指针,但没有实例化小部件本身(指针值在这里是nullptr
),这是可以的,因为此时应用程序尚未创建。这些行:
win = std::make_unique<Gtk::Window>();
m_button = std::make_unique<Gtk::Button>("Hello World");
实例化小部件(在堆上)并将它们分配给我们的唯一指针。这 必须 发生在应用程序创建之后,否则你将像以前一样获得段错误。
使用这个解决方案,注意所有语法都变成了指针语法(使用*
访问小部件和->
调用其方法)。
解决方案2(栈)
你可以使用返回你的小部件的static
实例的自由函数。将static
变量的创建包装在函数中允许你控制这些变量的创建时间(在堆栈上)。使用static
将确保小部件在整个程序的生命周期内存在(如果这是你想要的)。
例如:
#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/application.h>
#include <iostream>
Gtk::Window& TopLevelWindowGet()
{
static Gtk::Window window;
return window;
}
Gtk::Button& TopLevelButtonGet()
{
static Gtk::Button button("Hello World");
return button;
}
int main (int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
Gtk::Window& win = TopLevelWindowGet();
Gtk::Button& m_button = TopLevelButtonGet();
m_button.signal_clicked().connect([](){
std::cout << "Hello World" << std::endl;
});
win.set_border_width(10);
win.add(m_button);
m_button.show();
return app->run(win);
}
这些行:
Gtk::Window& TopLevelWindowGet()
{
static Gtk::Window window;
return window;
}
Gtk::Button& TopLevelButtonGet()
{
static Gtk::Button button("Hello World");
return button;
}
不会 创建小部件。只有在调用函数时才会发生这种情况。这就是为什么这些行在应用程序创建之后添加的原因:
Gtk::Window& win = TopLevelWindowGet();
Gtk::Button& m_button = TopLevelButtonGet();
结论
如果我必须选择一个解决方案,我会选择其中任何一个都不选。事实上,我会完全避免“顶级”变量(通常称为全局变量)。它们可能会创建令人讨厌的错误,特别是如果你编写并发代码。不过,如果非要选择一个解决方案,我更喜欢std::unique_ptr
解决方案。为什么呢?因为std::unique_ptr
容器设计用于以不会引发问题的方式保存资源,比如小部件。内存由智能指针处理,而不是由你处理。它还能让你更好地控制小部件的寿命。例如,通过调用std::unique_ptr::reset()
,你可以销毁小部件(如果它们不再需要的话)。
英文:
The reason for the segfault is indeed what was mentioned by @Retired Ninja in the comments. You should not try to instantiate widgets before the toolkit has been initialized, through a call to Gtk::Application::create
. That being said, you could use "top level" variables.
Solution 1 (heap)
You can use std::unique_ptr
(specifically designed to own a single resource, such as a widget) to store your "top level" variables.
For example:
#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/application.h>
#include <iostream>
std::unique_ptr<Gtk::Window> win;
std::unique_ptr<Gtk::Button> m_button;
int main (int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
win = std::make_unique<Gtk::Window>();
m_button = std::make_unique<Gtk::Button>("Hello World");
m_button->signal_clicked().connect([](){
std::cout << "Hello World" << std::endl;
});
win->set_border_width(10);
win->add(*m_button);
m_button->show();
return app->run(*win);
}
These lines:
std::unique_ptr<Gtk::Window> win;
std::unique_ptr<Gtk::Button> m_button;
instantiate unique pointers to a window and a button, but do not instantiate the widgets themselves (the pointer value is nullptr
here), which is fine because at this time, the application has not been created. These lines:
win = std::make_unique<Gtk::Window>();
m_button = std::make_unique<Gtk::Button>("Hello World");
instantiate the widgets (on the heap) and assign them to our unique pointers. This must happen after the application has been created otherwise you will get a segfault as before.
With this solution, notice how all syntax has turned to pointer syntax (using *
to access the widgets and ->
to call methods on it).
Solution 2 (stack)
You could use free functions that return static
instances of your widgets. Wrapping the creation of the static
variables in functions allows you to control the time of creation of these variables (on the stack). The use of static
will make sure the widgets live for the whole lifespan of the program (if this is what you want).
For example:
#include <gtkmm/button.h>
#include <gtkmm/window.h>
#include <gtkmm/application.h>
#include <iostream>
Gtk::Window& TopLevelWindowGet()
{
static Gtk::Window window;
return window;
}
Gtk::Button& TopLevelButtonGet()
{
static Gtk::Button button("Hello World");
return button;
}
int main (int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
Gtk::Window& win = TopLevelWindowGet();
Gtk::Button& m_button = TopLevelButtonGet();
m_button.signal_clicked().connect([](){
std::cout << "Hello World" << std::endl;
});
win.set_border_width(10);
win.add(m_button);
m_button.show();
return app->run(win);
}
These lines:
Gtk::Window& TopLevelWindowGet()
{
static Gtk::Window window;
return window;
}
Gtk::Button& TopLevelButtonGet()
{
static Gtk::Button button("Hello World");
return button;
}
do not create the widgets. This will only happen if the functions are called. This is why these lines are added after the application has been created:
Gtk::Window& win = TopLevelWindowGet();
Gtk::Button& m_button = TopLevelButtonGet();
Conclusion
If I had to pick a solution, I would choose none of them. In fact, I would avoid "top level" variables (or globals as they are usually called) altogether. They can create nasty bugs, especially if you write concurrent code.
With a gun to my head, however, I have a preference for the std::unique_ptr
solution. Why? Because the std::unique_ptr
container is designed to hold resources such as widgets in a way that heap is not a problem. Memory is handled by the smart pointer, not by you. It also gives you more control over the lifespan of the widgets. By calling std::unique_ptr::reset()
, for example, you can destroy you widgets (if they are no longer needed, for example).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论