=============================================================
标题:动态创建对象
日期:2011.4.27
文献:Thinking in C++
姓名:朱铭雷
=============================================================
———————————————————————————————————————
内存分配
———————————————————————————————————————
动态创建一个类的实例对象(于堆中)——采用c库函数
步骤
1 调用malloc申请内存,特别要注意所要申请内存的大小。
2 检验返回指针,确保内存成功分配。
3 初始化对象。
可以看出,采用c库函数动态创建类的对象很不方便,易出错。
———————————————————————————————————————
operator new (new操作符)
CBookInfo *pBookInfo = new CBookInfo;
new操作符会首先调用malloc分配内存,并且会检查内存分配是否成功。之后将所申请的内存地址,传递给CBookInfo类的构造函数,以初始化这块内存。总之,它简化了“动态申请内存”,也同时减少了出错几率。
———————————————————————————————————————
operator delete (delete操作符)
与new配对使用。
delete pBookInfo;
pBookInfo = NULL; // #define NULL 0
delete操作符首先调用CBookInfo类析构函数,然后调用free释放内存。将pBookInfo置为NULL(0),是为了防止删除pBookInfo指针两次,对同一个对象删除delete两次可能会出现问题,而“delete 0;”是安全的,因为它什么都不做。
———————————————————————————————————————
delete void*
void *pBookInfo = new CBookInfo;
delete pBookInfo;
pBookInfo = NULL;
这里的delete操作只会释放CBookInfo对象内存,但不会调用CBookInfo析构函数,因为编译器不知道该去调用哪个析构函数。
———————————————————————————————————————
动态创建对象时,小心使用void指针
在使用之前,应将其转换为适当(实际)类型,否则可能会丢失类型信息。在删除之前也要将其转换为适当(实际)类型,否则可能会产生内存泄露。
void *p = new string("abc");
cout << *(string*)p << endl;
delete (string*)p;
p = NULL;
———————————————————————————————————————
operator delete用于数组
CBookInfo *pBookInfo = new CBookInfo[10];
delete pBookInfo;
pBookInfo = NULL;
编译器不知道pBookInfo实际上存储的是指向一个动态创建的数组的起始地址,所以最终只对数组中的第一个CBookInfo对象调用了析构函数。正确的书写方式:
delete [] pBookInfo;
方括号的作用是使编译器取回动态创建数组时存放在某处的对象数量,以便为数组中所有的对象调用析构函数。
———————————————————————————————————————
new-handler
当new操作符无法找到足够大的内存时,将调用new-handle函数,该函数默认产生一个异常。程序员可以调用标准库函数set_new_handler来设置自己的new-handler。
#include <new>
void out_of_memory()
{
cerr << "memory exhausted" << endl;
exit(1);
}
set_new_handler(out_of_memory);
———————————————————————————————————————
重载new和delete
上面已经知道,new操作符将首先使用operator new()分配内存,然后调用对象的构造函数。构造函数的调用由编译器完成,程序员可以改变的只是如何分配内存。重载delete的情况也是类似。
———————————————————————————————————————
重载全局的new和delete
重载全局的new和delete要慎重,将导致默认版本完全不能被访问,出了问题就是很严重的问题。
operator new()和operator delete()的normal signature:
void* operator new(size_t size);
void operator delete(void *p);
示例:
void* operator new(size_t size)
{
void* m = malloc(sz);
if(!m) puts("out of memory");
return m;
}
void operator delete(void* m)
{
free(m);
}
———————————————————————————————————————
为了一个特定的类重载new和delete
在一个类中重载new和delete,即使不显示使用static关键字,它们也仍然是一个静态的成员函数。如果一个类A重载了new,则使用new A创建对象时将使用类A的重载new而不是全局new。delete的情况也一样。如果某个类重载了new和delete,通常也会重载new[]和delete[],否则“new A[10];”将会调用全局new。
———————————————————————————————————————
一个完整的类重载new,new[],delete,delete[]示例
#include <new>
#include <fstream>
using namespace std;
ofstream trace("ArrayOperatorNew.out");
class Widget
{
static const int sz = 2;
int i[sz];
public:
Widget() { trace << "Widget()/n"; }
~Widget() { trace << "~Widget()/n"; }
void* operator new(size_t sz)
{
trace << "Widget::new: " << sz << " bytes/n" << endl;
return ::new char[sz];
}
void* operator new[](size_t sz)
{
trace << "Widget::new[]: " << sz << " bytes/n" << endl;
return ::new char[sz];
}
void operator delete(void* p)
{
trace << "Widget::delete/n" << endl;
::delete []p;
}
void operator delete[](void* p)
{
trace << "Widget::delete[]/n" << endl;
::delete []p;
}
};
int main()
{
Widget* w = new Widget;
delete w;
Widget* wa = new Widget[2];
delete []wa;
}
看下ArrayOperatorNew.out文件的输出内容:
Widget::new: 8 bytes
Widget()
~Widget()
Widget::delete
Widget::new[]: 20 bytes
Widget()
Widget()
~Widget()
~Widget()
Widget::delete[]
从输出信息可以看出:
1 new(new[])操作会首先调用Widget::new(Widget::new[])分配内存,然后调用Widget()构造函数初始化内存。
2 delete(delete[])操作会首先调用~Widget()析构函数,然后调用Widget::delete(Widget::delete
[])释放内存。
3 “new Widget[2]”实际申请了20个字节,额外多申请了4个字节,这4个字节用来保存数组信息,尤其是数组中存放的对象数量,以备delete []使用。
———————————————————————————————————————
重载多参数的new和delete
如:
void* operator new(size_t s, char* pszFileName, int iLineNum);
void operator delete(void* p, char* pszFileName, int iLineNum);
第一个参数和返回值类型不要改动,比如new的第一个参数,由编译器计算需要分配多少内存,传递给new。
———————————————————————————————————————
在MFC中重载全局new和delete
这个会有所不同,可能会遇到一定的麻烦。
———————————————————————————————————————