——读 GTKMM Tutorial 笔记。
by Yazy, 2005-6-4
1、传统 C++ 内存管理方式
1. 类作作用域内的构件
将构件声明为类的成员变量,不使用动态内存分配机制。使用这种方式的优点是“memory
management is grouped in one place.”,随着类的创建与销毁,构件也随之存在与消失。
// From the tutorial:
#include <gtkmm/button.h>
class Foo
{
private:
Gtk::Button theButton;
// will be destroyed when the Foo object is destroyed
};
我用这种方式使用构件时出现问题。我从 Gtk::Window 继承类 HelloWorld,并在HelloWorld
里声明两个或者两个以上的构件,比如 Gtk::Button 时,编绎出来的程序会 crash。折腾了好
久,未有的确结论。现在只猜测是堆栈益出(要求过多堆栈空间,系统未能满足),因为从main
的第一行开始,我所有的对象都是在堆栈里的,main 内容大致如下:
int main(...) {
Gtk::Main kit(argc, argv);
HelloWorld hello;
kit.run(hello);
return 0;
}
程序运行时上面 hello 将存在于堆栈中,其成员(复合型)也当然如此。若 HelloWorld 有
m_button1 及 m_button2 两个成员,那它们也都存在于堆栈之中,所以我猜想我的程序
crashed 于堆栈益出。但是……一个 Gtk::Button 对象会很大吗?或者说两个构件就足以用光整
个堆栈吗?想到那几成员构件的大小,我对自己这样的猜测感到怀疑。我可以确认我的程序运行
之时内存还没达到“高危”境况。
同样的因堆栈要求过大而导致程序崩溃的情况我以前在 MFC 环境也遇到过。当我在一个函数作
用域内一下子要求 2048 Bytes 作为缓冲时,程序便垮了。直至我减小要求的缓冲大小,情况才
正常。
此次的开发平台与当时不一样,现在我用的是 MinGW 的 GCC(3.4.2),不知道编绎平台的不
同会不会影响程序堆栈大小的不同。
当我将 HelloWorld 的几个构件成员都换成堆对象(并将对象交由 GTKMM 托管,即下文出现的
内容)时,程序便不再烦人了。
2. 函数作用域内的构件
即在函数体内使用堆栈构件。这种方式的优点是“the increased data hiding and reduced
dependencies(提高了数据隐藏、减少了依赖性)”。同样,如果堆栈大小有限,那这种方式
同样也会使用程序崩溃。
3. 使用 new 和 delete 动态分配
使用堆对象,要求程序员手动地配对使用 new 和 delete,不然便会造成 Memory Leak。堆一
般认为是不会不够用的,使用它的麻烦在于 new 和 delete 配对的麻烦,容易造成失误。
2、托管构件
托管构件是使用 new 创建的堆构件,但对构件的内存回收都交给 GTKMM 来完成(准确地说是容器
构件),所以程序员不必调用 delete,不必担心配对问题。容器在自身销毁之时会销毁其管理之下的
子构件。
// From the tutorial:
MyWidget::MyWidget()
{
Gtk::Button* pButton = manage(new Gtk::Button("Test"));
add(*pButton); //add aButton to MyWidget
}
上面的 manage 是容器的方法,GTKMM的所有构件都有 set_manage 方法,可以将自己委托于父
构件:
// From the tutorial:
foo.add( (w=new Gtk::Label("Hello"), w->set_manage(), &w) );
// is the same as the following, but more tedious
foo.add( manage(new Gtk::Label("Hello")) );
Top Level 容器不能成为其它容器的子构件,所以这类容器不能委托于“人”,如果使用将它们放于
堆中,我们必须考虑上面的问题。