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

gtkmm widgets - use smartpointers or pointers?

我正在尝试学习如何使用 gtkmm 并基本掌握了 C++(我喜欢挑战!)。我一直在努力完成教程(以及其他阅读材料)。我正在尝试使用使用 glade 设计 UI 然后编写代码来完成工作的方法。

所以我构建了一个非常简单的 UI(现在是 window 和按钮!)。我正在使用 GTK::Builder 从文件加载 UI。我将代码分为 classes 和一个主要调用者。

这是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
}

这里是 HelloWindow headerclass

#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

这里是 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;
}

一切正常,我想我明白发生了什么。但是,我看到了一种在运行时添加小部件的不同方法 (https://sodocumentation.net/gtk3/topic/5579/using-glade-with-builder-api)。区别在于按钮 object 的声明方式。在上面的代码中,它被声明为指向行中按钮 object 的指针:

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

但是,上面的网站使用了指向按钮的智能指针的方法object:

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

第二种方法似乎不太清楚,特别是 cast_dynamic 方面,有人可以解释一下这两种方法之间的区别吗?

希望我提供了足够的信息。

谢谢

马丁

首先要了解区别,因为你是新手 C++,我建议阅读以下主题:

第一种方法:get_widget

通过这种方法,您将获得一个原始指针(而不是智能指针 指针)指向从 ui 文件定义的 小部件 。示例:

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

在此之后,pGrid 指向一个 Gtk::Grid 小部件。请注意,没有 铸造是要求 ui 红色,你立即得到一个 Gtk::Grid。这是因为 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 
                              )

通常在 C++ 中,例如 原始指针可能是危险的,因为如果它们指向一个对象 分配在堆上(通常使用new),一定要记住使用 delete 完成后在它们上面,否则会发生内存泄漏。在这个 在这种情况下,pGrid 确实是指向分配在 堆,但是 documentation states:

Note that you are responsible for deleting top-level widgets (windows and dialogs) instantiated by the Builder object. Other widgets are instantiated as managed so they will be deleted automatically if you add them to a container widget.

所以大部分时间(即如果不是顶层小部件),你没有 打电话给 delete,但有时你会这样做。这是因为 Gtkmm 有设施 自动 delete 销毁对象。请参阅 Gtk::manage 有关此的更多信息。

何时或不使用 delete 可能随着代码的增长而变得困难,尤其是因为 你不必总是这样做(它很容易忘记)。

第二种方法:get_object

通过这种方法,您将获得一个智能指针 (a Glib::RefPtr) 一个 object 并转换为正确的类型是 required,因此 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"));

这样做的好处是,一旦施法完成, 您不必管理对象的内存。它由 智能指针(即智能指针会自动调用 delete 为你。即使对于“顶级”小部件也是如此。

注意:此处使用的转换 cast_dynamic 是 Gtkmm 特定的转换,它包装了 dynamic_cast.

我的看法

我个人会选择第二种方法,因为你会自动 内存管理(即使对于顶级小部件),因此没有内存泄漏。 但是,正如您已经注意到的那样,代码变得更难阅读。