Chapter 22. メモリ管理

Table of Contents

ウィジット

通常のC++でのメモリ管理

gtkmmではプログラマがウィジットのライフタイム(つまり、生成と破棄の時間)を他のC++オブジェクトと同様に管理することを許しています。この柔軟性のおかげで、newdeleteを使って動的にオブジェクトを生成・破棄したり、通常のクラスメンバとして扱ったり(クラスが破棄されたときに自動的に破棄される)、ローカルなインスタンスとして扱ったり(インスタンスがスコープを抜けると破棄される)することができます。他のC++ GUIツールキットではメモリ管理は一部分のみに制限されていますので、このような柔軟性がありません。

ここではいくつか通常のC++でのメモリ管理の例を挙げます:

クラススコープのウィジット

プログラマが動的なメモリ割り当てを必要ないと思うならば、クラススコープで自動変数のウィジットが使えます。これを使う利点の一つは、メモリ管理が一つの場所で行われるということです。プログラマはウィジットをdeleteし忘れてメモリリークを起こすリスクを負わずにすみます。

クラススコープウィジット使用の最も不便な点は、クラスヘッダでのクラス設計よりも、クラス実装で明らかになります。クラススコープのウィジットも自動変数のウィジットを要求しますから、他のC++のクラススコープ自動変数が持っているのと同じ不便さがあります。

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

関数スコープのウィジット

プログラマーがクラススコープのウィジットを必要としていないのなら、関数スコープのウィジットが使えるかもしれません。クラススコープに対する関数スコープの利点は、データを隠蔽できることと依存性を減らせることです。

{
  Gtk::Button aButton;
  aButton.show();
  ...
  kit.run();
}

newとdeleteによる動的割り当て

しかしながら、一番良くあるケースですが、プログラマはmanage()(後述)を使ってコンテナに自動的に子ウィジットを破棄させるやり方を好むでしょう。もっとも、manage()を使わなくてはならないわけではありません。伝統的なnew演算子、delete演算子も使えます。

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

// do something useful with pButton

delete pButton;

ここではpButtonをdeleteしてメモリリークを防いでいます。

管理されたウィジット

もう一つの選択肢として、ウィジットを破棄した時にウィジットコンテナを操作できます。最もよくあるケースは、あるウィジットを、それを納めているコンテナが存在している間だけ存在させたいというものです。ウィジットの寿命の管理をそのコンテナに委譲するには、まずウィジットをmanage()と共に生成し、add()でコンテナに加えてください。そうすればウィジットはコンテナが破棄されたときに、同様に破棄されます。

manage()とadd()による動的割り当て

gtkmmmanage()関数とadd()メソッドをウィジットを生成・破棄するために提供しています。トップレベルにあるウィンドウ以外の全てのウィジットは、表示するためにコンテナに納めなければいけません。manage()関数はウィジットに印をつけて、コンテナに納められた時にそのコンテナがウィジットを破棄するようにします。

MyWidget::MyWidget()
{
  Gtk::Button* pButton = manage(new Gtk::Button("Test"));
  add(*pButton); //add aButton to MyWidget
}

さて、MyWidget型のオブジェクトが破棄されたとすると、そのボタンもまた破棄されます。pButtonをdeleteしてメモリを解放する必要はありません。delete作業はMyWidgetオブジェクトに委譲されています。

さらに、gtkmmset_manage()メソッドを全てのオブジェクトに対して提供しています。このメソッドの使用はmanage()と同じ結果をもたらしますが、やや冗長になります:

foo.add( (w=new Gtk::Label("Hello"), w->set_manage(), &w) );

これは以下と同等です。

foo.add( manage(new Gtk::Label("Hello")) );

当然のことながらトップレベルのコンテナを別のコンテナに納めることはできません。プログラマにはトップレベルのコンテナを伝統的なC++の機能を使って破棄する責任があります。例えば、トップレベルのウィンドウはコードの中ではmain()関数内の単なるインスタンスになるでしょう。