简体   繁体   English

gtkmm 小部件 - 使用智能指针或指针?

[英]gtkmm widgets - use smartpointers or pointers?

I am trying to learn how to use gtkmm having got a basic grasp of C++ (I like a challenge.).我正在尝试学习如何使用 gtkmm 对 C++ 有基本的了解(我喜欢挑战。)。 I have been working my way through the tutorials (as well as other reading).我一直在努力完成教程(以及其他阅读)。 I am trying to use the approach of using glade to design the UI and then write the code to do the work.我正在尝试使用 glade 的方法来设计 UI,然后编写代码来完成这项工作。

So I have built a very simple UI (window and button at the moment.): I am using the GTK:.Builder to load the UI from file.所以我构建了一个非常简单的 UI(目前是窗口和按钮。):我正在使用 GTK:.Builder 从文件加载 UI。 I am dividing the code into classes and a main caller.我将代码分为类和主调用者。

Here is the main.cpp这是main.cpp

#include "hellowindow.h"
#include <gtkmm/application.h>

int main(int argc, char *argv[]) {
    auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); //creates a Gtk::Application object, stored in a Glib::RefPtr smartpointer, create() method for this object initializes gtkmm.
    HelloWindow hw; // Create a HelloWindow object
    return app->run(hw, argc, argv); // shows the HelloWindow object and enter the gtkmm main processing loop, will then return with an appropriate success or error code
}

here is the header for the HelloWindow class这是 HelloWindow class 的 header

#ifndef HELLOWINDOW_H
#define HELLOWINDOW_H

#include <gtkmm/application.h>
#include <gtkmm/applicationwindow.h>
#include <gtkmm/button.h>
#include <gtkmm/box.h>
#include <gtkmm/builder.h>
#include <glibmm/fileutils.h>

/* derive the class from Gtk::ApplicationWindow base class */
class HelloWindow : public Gtk::ApplicationWindow {

public:
  /* Conctructor */
  HelloWindow();

  /* Destructor */
  ~HelloWindow() override;

protected:
  /* Signal handlers: */
  void on_button_clicked();

  /* Member widgets: */
  Gtk::Box *cont;                             // Container
  Gtk::Button *pButton;                       // Pointer to a Button
  Glib::RefPtr<Gtk::Button> display_btn;      // Smart pointer to a Button
  Glib::RefPtr<Gtk::Builder> builder;         // Builder
};

#endif // HELLOWINDOW_H

and here is the class code:这是 class 代码:

#include "hellowindow.h"
#include <iostream>

HelloWindow::HelloWindow() : builder(Gtk::Builder::create()){
  try {
    /* load window from glade file */
    builder->add_from_file("glade/simple.glade");
  }
  catch(const Glib::FileError& ex) {
    /* catch file errors */
    std::cerr << "FileError: " << ex.what() << std::endl;
    return;
  }
  /* ui builder  created successfully from file */
  /* add a container to the builder */
  builder->get_widget<Gtk::Box>("cont", cont);

  builder->get_widget<Gtk::Button>("display_button", pButton);

  pButton->signal_clicked().connect(
    sigc::mem_fun(*this, &HelloWindow::on_button_clicked)
  );

  /* add the container to the application window */
  add(*cont);

  /* set some parameters for the window */
  set_title("Simple Gtk::Builder Demo"); // set the window title
  set_default_size(500, 500); // set the window size
  show_all(); // show the window and all of the enclosed widgets
}

HelloWindow::~HelloWindow(){
}

void HelloWindow::on_button_clicked(){
  std::cout << "Hello World" << std::endl;
}

This all works fine and I think I understand what is happening.这一切都很好,我想我明白发生了什么。 However, I have seen a different approach to adding widgets at runtime ( https://sodocumentation.net/gtk3/topic/5579/using-glade-with-builder-api ).但是,我看到了在运行时添加小部件的不同方法( https://sodocumentation.net/gtk3/topic/5579/using-glade-with-builder-api )。 The difference is how the button object is declared.不同之处在于按钮 object 的声明方式。 In the code above it is declared as a pointer to a button object in the line:在上面的代码中,它被声明为指向按钮 object 的指针:

builder->get_widget<Gtk::Button>("display_button", pButton);

However, the website above uses an approach of a smartpointer to the button object:但是,上面的网站使用智能指针的方法指向按钮 object:

display_btn = Glib::RefPtr<Gtk::Button>::cast_dynamic(builder->get_object("display_button"));

The second way of doing it seems less clear, specifically the cast_dynamic aspect, would someone please explain the difference between the two approaches?第二种方法似乎不太清楚,特别是 cast_dynamic 方面,有人可以解释这两种方法之间的区别吗?

I hope that I have included enough information.我希望我已经包含了足够的信息。

Thank you谢谢

Martyn马丁

First of all, to understand the difference, and since you are new to C++, I would recommend reading on the following topics:首先,要了解区别,并且由于您是 C++ 的新手,我建议您阅读以下主题:

First approach: get_widget第一种方法: get_widget

With this approach, you are getting a raw pointer (as opposed to a smart pointer) to a widget defined from the ui file.使用这种方法,您将获得指向从 ui 文件定义的小部件的原始指针(而不是智能指针)。 Example:例子:

Gtk::Grid* pGrid = nullptr;
refXml->get_widget("mygrid", pGrid);

After this, pGrid points to a Gtk::Grid widget.在此之后, pGrid指向一个Gtk::Grid小部件。 Notice that no casting is required, you immediately get a Gtk::Grid .请注意,不需要强制转换,您会立即获得Gtk::Grid This is because the Gtk::Builder::get_widget method is a template method:这是因为Gtk::Builder::get_widget方法是一个模板方法:

// T_Widget is like a placeholder for some widget type,
// like Gtk::Grid for example.
template <class T_Widget >
void Gtk::Builder::get_widget(const Glib::ustring& name,
                              T_Widget*& widget 
                              )

Usually in C++, such raw pointers may be dangerous because if they point to an object allocated on the heap (usually using new ), one must remember to use delete on them when done, otherwise memory leaks will occur.通常在 C++ 中,这样的原始指针可能是危险的,因为如果它们指向分配在堆上的 object(通常使用new ),必须记住在完成时对它们使用delete ,否则会发生 ZCD69B4957F06CD818D7BF3D61980E 泄漏。 In this case, pGrid is indeed a raw pointer to an object allocated on the heap, but the documentation states :在这种情况下, pGrid确实是指向在堆上分配的 object 的原始指针,但文档指出

Note that you are responsible for deleting top-level widgets (windows and dialogs) instantiated by the Builder object.请注意,您负责删除由 Builder object 实例化的顶级小部件(窗口和对话框)。 Other widgets are instantiated as managed so they will be deleted automatically if you add them to a container widget.其他小部件被实例化为托管,因此如果您将它们添加到容器小部件,它们将被自动删除。

so most of the time (ie if not a toplevel widget), you do not have to call delete , but sometimes you do.所以大多数时候(即如果不是顶级小部件),您不必调用delete ,但有时您会这样做。 This is because Gtkmm has facilities to automatically delete destroyed objects.这是因为 Gtkmm 具有自动delete被破坏对象的功能。 See Gtk::manage for more information on this.有关这方面的更多信息,请参阅Gtk::manage

When or not to use delete may become hard as code grows, especially since you do not have to always do it (it can become easy to forget).随着代码的增长,何时使用或不使用delete可能会变得困难,尤其是因为您不必总是这样做(它很容易忘记)。

Second approach: get_object第二种方法: get_object

With this approach, you are getting a smart pointer (a Glib::RefPtr ) to an object and casting to the right type is required, hence the cast_dynamic使用这种方法,您将获得指向 object 的智能指针( Glib::RefPtr ,并且需要转换为正确的类型,因此cast_dynamic

// get_object returns a Glib::RefPtr<Glib::Object>, which is not a Glib::RefPtr<Gtk::Button>
// so a cast is performed. This works because Gtk::Button is a child class of
// Glib::Object.
display_btn = Glib::RefPtr<Gtk::Button>::cast_dynamic(builder->get_object("display_button"));

The advantage of this is that once the cast is performed, you do not have to manage the object's memory.这样做的好处是,一旦执行了转换,您就不必管理对象的 memory。 It is managed by the smart pointer (ie the smart pointer will automatically call delete for you. This is true even for "top level" widgets.它由智能指针管理(即智能指针会自动为您调用delete 。即使对于“顶级”小部件也是如此。

Note: the cast used here, cast_dynamic , is a Gtkmm specific cast which wraps a dynamic_cast .注意:这里使用的演员cast_dynamic是一个 Gtkmm 特定的演员表,它包装了一个dynamic_cast

My opinion我的意见

I would personally go with the second approach because you get automatic memory management (even for top level widgets), and hence no memory leaks.我个人会使用第二种方法 go 因为您可以获得自动 memory 管理(即使对于顶级小部件),因此没有 memory 泄漏。 However, the code gets harder to read as you already noticed.但是,正如您已经注意到的那样,代码变得更难阅读。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM