【问题标题】:gtkmm widgets - use smartpointers or pointers?gtkmm 小部件 - 使用智能指针或指针?
【发布时间】:2021-02-10 15:55:25
【问题描述】:

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

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

这里是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 类的标题

#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

这是课程代码:

#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)。不同之处在于按钮对象的声明方式。在上面的代码中,它被声明为指向按钮对象的指针:

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

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

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

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

我希望我已经包含了足够的信息。

谢谢

马丁

【问题讨论】:

    标签: c++ memory-management smart-pointers gtkmm


    【解决方案1】:

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

    第一种方法:get_widget

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

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

    在此之后,pGrid 指向一个 Gtk::Grid 小部件。请注意,没有 需要强制转换,您会立即获得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

    请注意,您负责删除顶级小部件(windows 和对话框)由 Builder 对象实例化。其他小部件是 实例化为托管,因此如果您将它们自动删除 将它们添加到容器小部件中。

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

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

    第二种方法:get_object

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

    我的看法

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

    【讨论】:

    • 感谢您的详细回复。我会做更多的阅读 - 感谢您的指点(请原谅双关语!)
    猜你喜欢
    • 1970-01-01
    • 2014-07-25
    • 1970-01-01
    • 2017-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 1970-01-01
    相关资源
    最近更新 更多