文章目录
- new是怎么调用的?
- 那么delete呢?
- new[]和delete[]为什么要成对使用?
- 注意到了operator new和operator delete~
- new()怎么用?
- delete()有点复杂
new是怎么调用的?
这里是一条new的使用语句:
A *pc = new A(1, 2);
这条语句,编译器会转化为下面样子:
A* pc;
try {void* mem = operator new(sizeof(A)); //申请内存pc = static_cast<A*>(mem); //类型转换pc->A::A(); //调用构造函数
}
catch (std::bad_alloc) {......
}
所以,当我们使用new的时候,它有两个作用:一是申请内存,二是调用相应的构造函数。
那么delete呢?
这是一条司空见惯的delete的使用语句:
delete pc;
一样的,编译器也将该语句转化为下面这样:
pc->~A(); //调用析构函数
operator delete(pc); //释放内存
那么,delete的作用就是先调用相应的析构函数,再释放内存。
new[]和delete[]为什么要成对使用?
调用了new[]为什么一定要调用delete[]?对于不包含指针的成员变量的类来说可能没有影响。但是对于包含有指针的成员变量来说通常都存在影响(见下图)。
string *pStr = new String[3];
delete[] pStr;
上图是new出对象的内存分布图。
因为delete[]会唤起多次析构函数,当存在带有指针变量时,在delete[]时需要先调用析构函数处理这些指针指向的空间。如果上图仅仅执行delete pStr,表示只调用了一次析构函数,处理了其中的一个指针指向的空间后就进行了释放了pStr所指向的内存,这样就造成了内存泄露。
注意到了operator new和operator delete~
这两个函数是 C++ 语言标准库的库函数,原型如下:
void *operator new(size_t); //allocate an object
void *operator delete(void *); //free an objectvoid *operator new[](size_t); //allocate an array
void *operator delete[](void *); //free an array
具体代码这里就先不谈,只需要知道operator new和operator new[]调用了malloc,operator delete和operator delete[]调用了free即可(嗯,很多创建删除新对象什么的函数最后的指向都是malloc和delete)。
同时,以上四个函数都是可以重载的,一般默认调用的是全局的operator new,operator new[],operator delete和operator delete[]。但是,我们对这两个函数进行重载的时候,很少会对全局的operator new,operator new[],operator delete和operator delete[]进行重载,这样做的话,那影响可太大了。因此,我们都是在类中对这两个函数进行重载(记得是static,不过编译器也默认为该类函数是static,毕竟对象都没有,怎么由对象调用该函数创建对象)。
new()怎么用?
首先看看这段代码:
char *buf = new char[sizeof(A)*3];
A *pc = new(buf) A(1,2);
这段代码中的new()也称作定点new,这是因为这个new不需要自己申请空间,直接就在我们传进去的buf里调用构造函数创建对象。被编译器转化为如下:
A* pc;
try {void* mem = operator new(sizeof(A), buf); //申请内存pc = static_cast<A*>(mem); //类型转换pc->A::A(); //调用构造函数
}
catch (std::bad_alloc) {......
}
而这里的operator new的代码如下:
void* operator new(size_t, void* loc)
{return loc;
}
没有自己申请空间,只是将loc的地址返回。
但是new()还有别的形式,这些可不是定点new。比如下列代码:
A *pA = new(100, 'a') A;
也就是重载了类成员operator new(),使其参数列表不同。但是第一个参数必须是size_t,其余参数以new所指定的参数为初值(这是因为没有小括号的时候,类的代大小会被传递给operator new作为第一个参数,此时的类型就是size_t)。
delete()有点复杂
当然,我们也可以重载类成员operator delete(),但是一般来说是绝对不会被delete调用的。只有当new所调用的构造函数抛出异常时,才会调用这些相对应的重载的operator delete()。这样设计的目的是归还未能完全创建成功的object所占用的memory。