部件

23.1.1. 标准C++内存管理

gtkmm允许程序员像管理任意其他C++对象一样对任意部件的生命周期(即构造和析构)进行控制。这种灵活性使你可以用newdelete动态创建和销毁对象,或使用常规的类成员(销毁类时部件将被自动销毁),或使用局部实例(当实例超出其作用域时被销毁)。这种灵活性在某些C++ GUI工具包中不存在,这些工具包限制程序员只能使用C++内存管理特性中的一部分。

以下是一些使用标准C++进行内存管理的示例:

23.1.1.1. 类作用域部件

如果程序员不需要动态分配内存,则可以在类作用域中使用具有自动储存期的部件。在类作用域中使用自动储存期的部件的优点之一是内存管理被集中在一个地方。程序员不需要承担因delete部件出现差错而导致内存泄漏的风险。

使用类作用域部件的主要缺点是在包含类声明的头文件中展示了类的实现而不只是类的接口。

#include <gtkmm/button.h>
#include <gtkmm/window.h>
class Foo : public Gtk::Window
{
private:
  Gtk::Button theButton;
  // will be destroyed when the Foo object is destroyed
};

23.1.1.2. 函数作用域部件

如果程序员不需要类作用域部件,那么也可以使用函数作用域部件。函数作用域相比于类作用域的优点是增加了数据的隐蔽性和隐藏了依赖。

{
  Gtk::Button aButton;
  aButton.show();
  ...
  app->run();
}

23.1.1.3. 使用new和delete进行动态分配

通常,程序员更喜欢使用Gtk::make_managed()(参见下文)创建允许容器自动销毁的子部件。这并非强制性的要求,你也可以使用newdelete操作符,但是现代C++风格鼓励使用更安全的内存管理模型,因此最好使用Gtk::make_managed()创建部件,并让他们的父部件销毁它们,而不是手动执行动态内存分配。

auto pButton = new Gtk::Button("Test");

// do something useful with pButton

delete pButton;
在这,程序员删除pButton以防止内存泄漏。

23.1.2. 托管部件

另外,你可以让部件的容器控制部件被销毁的时机。多数情况下你会希望部件的生存周期与包含它的容器的生命周期一样长。要将部件的生命周期管理委托给其容器,请使用Gtk::make_managed()创建部件,然后使用诸如Gtk::Box::append()这类的方法将其装入容器中。这样部件就会随着容器一并销毁。

23.1.2.1. 使用make_managed()和append()进行动态分配

gtkmm提供了包括make_managed()函数和Gtk::Box::append()方法在内的用于简化创建和销毁部件的很多方法。这些部件的生命周期可以由其父部件进行管理。

除了顶级窗口以外的任何部件都需要添加到一个父容器中才能够被显示。manage()函数对部件进行标记,以便将该部件添加到父容器时,由其父容器负责删除该部件,这意味着用户不需要如此做。创建生命周期由其父部件进行管理的部件的原始方法是调用manage(),并将向其传入使用new表达式创建的动态分配的部件。

但是通常当你创建这样的部件时,你已经知道其父部件应该负责销毁它。此外现代C++风格不推荐使用new操作符,而将新创建的部件传递给manage()时需要使用new。因此,gtkmm添加了make_managed()函数,这个函数将两个步骤合并到了一起。这避免了让你编写现代C++风格不建议的带有new的代码,并更清楚的表达了创建托管部件的意图。

MyContainer::MyContainer()
{
  auto pButton = Gtk::make_managed<Gtk::Button>("Test");
  append(*pButton); //add *pButton to MyContainer
}

现在,当你销毁MyContainer类型对象时,该按钮也被销毁。不再需要主动deletepButton以释放其内存;对其的删除操作委托给了MyContainer对象。

请注意,如果你从未将该部件添加到任何父容器中,或者你添加后在其父容器调用了Gtk::Container::remove()将其从父容器中删除了,gtkmm将会使部件的生命周期管理恢复到调用manage()之前的状态,这通常意味着删除部件的责任被归还给了用户。

当然,顶级容器不会被添加到另一个容器中。程序员负责使用一种传统的C++技术销毁顶级容器。例如,你的顶级窗口可能只是main()函数中的一个实例。