简体   繁体   中英

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.). 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.

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. I am dividing the code into classes and a main caller.

Here is the 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

#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:

#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 ). The difference is how the button object is declared. In the code above it is declared as a pointer to a button object in the line:

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

However, the website above uses an approach of a smartpointer to the button 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?

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:

First approach: 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. Example:

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

After this, pGrid points to a Gtk::Grid widget. Notice that no casting is required, you immediately get a Gtk::Grid . This is because the Gtk::Builder::get_widget method is a template method:

// 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. In this case, pGrid is indeed a raw pointer to an object allocated on the heap, but the 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.

so most of the time (ie if not a toplevel widget), you do not have to call delete , but sometimes you do. This is because Gtkmm has facilities to automatically delete destroyed objects. See Gtk::manage for more information on this.

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).

Second approach: 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

// 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. 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.

Note: the cast used here, cast_dynamic , is a Gtkmm specific cast which wraps a 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. However, the code gets harder to read as you already noticed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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