2008年10月14日星期二

[TIC++] C13. Dynamic Object Creation

代码阅读<Thinking In C++>
Chapter 13. Dynamic Object Creation

一、在堆区创建内存

// c13: malloc_class.cpp
// malloc with class objects
#include <cstdlib>
#include <cstring>
#include <cstring>
#include <iostream>
using namespace std;

class obj {
int i, j, k;
enum { sz = 100 };
char buf[sz];
public:
void initialization() { // can't use constructors
cout << "initialization obj" << endl;
i = j = k = 0;
memset(buf, 0, sz);
}
void destroy() {
cout << "destroy obj" << endl;
}
};

int main()
{
obj *o = (obj*)malloc(sizeof(obj));
if (o == 0)
return -1;
o->initialization();
//sometimes later...
o->destroy();
free(o);
}
/* result:
initialization obj
destroy obj
*/

当C++对象创建的时候,将会发生两件事情:
1) 为空间分配内存
2) 调用构造方法来初始化这块内存

而空间创建问题(1),可以有以下选择:
1) 在程序开始前创建静态空间,位于静态内存区,拥有文件生命期
2) 在执行点处创建堆栈空间,他将在执行跳出方法的时候释放。堆栈的处理是处理器完成
的,自然非常高效
3) 在执行点处创建堆空间(heap),这被成为动态内存分配(!)。方法必须在运行调用的时候
创建堆空间,这意味着你可以决定任何时候创建,当然也要为释放负全权责任————生命期完
全由程序员掌控。

上面的例子展示了C处理堆区空间的方法:
这里的obj o对象的创建,没有使用到构造方法!这是非常糟糕的情况,因为你有可能选择
是否初始化,甚至可能忘记,初始化的丢失极为可能带来bug。

于是C++引入了一种新方法:

// c13: new_and_delete.cpp
#include <iostream>
using namespace std;

class tree {
int height;
public:
tree(int tree_height): height(tree_height) {}
~tree() { cout << "*"; }
friend ostream& operator <<(ostream &os, const tree* t) {
return os << "tree height is: "
<< t->height << std::endl;
}
};

int main(void)
{
tree *t = new tree(40);
cout << t;
delete t;
}
/* result
tree height is: 40
*/

new和delete保证了构造方法的正常调用,甚至还会检查内存申请是否调用成功。
这一切显得像堆栈内存分配一样简单。

二、delete void*

// c13: bad_void_pointer_deletion.cpp
#include <iostream>
using namespace std;

class object {
void *data;
const int size;
const char id;
public:
object(int sz, char c): size(sz), id(c) {
data = new char[size];
cout << "constructing object" << id
<< ", size = " << size << endl;
}
~object() {
cout << "desctructing object " << id << endl;
delete []data; // OK, just release storage;
// no desctructor calls are necessary;
}
};

int main() {
object *a = new object(40, 'a');
delete a;
void *b = new object(40, 'b');
delete b;
}
/* result:
constructing objecta, size = 40
desctructing object a
constructing objectb, size = 40
*/

object是一个包含 void *data的一个类,在这个类的析构方法中,我们看到使用了delete
来析构data,这没什么问题,因为这里要作的只是释放内存。

但是在main()中,对于delete来说,对象类型信息就非常必要了。由于delete a知道a是一
个class object,所以他使用到了~object()析构方法,data也被释放了。可是void *b就遇
到麻烦,他无法得知void*的类型信息,就无法调用析构方法,因此data将会永远的丢失,
这是一个静静的内存泄露。

如果你在C++工程中有内存泄露问题,那就要好好检查你的delete了。

三、介绍new handler

// c13: new_handler.cpp
#include <iostream>
#include <cstdlib>
#include <new>
using namespace std;

int count = 0;

void out_of_memory()
{
cerr << "memory exhausted after " << count << " allocations" << endl;
exit(1);
}

int main()
{
set_new_handler(out_of_memory);
while(1) {
count++;
new int[1000];
}
}

当new无法找到一个足够大的堆空间来存放对象,会出现何种情况呢?
这样的情况下,就会调用new-handler。默认的new-handler会抛出异常,当然可以定义新的
new-handler,打印出信息告诉你发生了什么。

这里就是用<new>的set_new_handler设置了方法,他会打印在经历多少次存储之后耗尽内存。

_

没有评论:

发表评论