我们首先来看一下下面的简单示例
#include <iostream>
class MTestClass
{
public:
MTestClass(int num) : m_Number(num) {
std::cout << "执行构造函数 MTestClass(int num)" << std::endl;
}
~MTestClass() {
std::cout << "执行析构函数" << std::endl;
}
void print(void) {
std::cout << "Number is : " << m_Number << std::endl;
}
private:
int m_Number = 10;
};
int main(int argc, char** argv)
{
MTestClass *p = new MTestClass(50);
p->print();
delete p;
system("pause");
return 0;
}
上面的代码很简单,
在代码中我们 new 了一个对象 p,
然后调用函数 p->print() ,
最后将对象删除, delete p;
上面的程序运行结果是这样的:
执行构造函数 MTestClass(int num)
Number is : 50
执行析构函数
我们可以对这个过程做出如下猜测的操作步骤:
我们改写上面的示例:
#include <iostream>
class MTestClass
{
public:
MTestClass(int num) : m_Number(num) {
std::cout << "执行构造函数 MTestClass(int num)" << std::endl;
}
~MTestClass() {
std::cout << "执行析构函数" << std::endl;
}
void* operator new(size_t size) {
std::cout << "执行 operator new !" << std::endl;
return ::operator new(size);
}
void operator delete(void* pWhere) {
std::cout << "执行 operator delete !" << std::endl;
return ::operator delete(pWhere);
}
void print(void) {
std::cout << "Number is : " << m_Number << std::endl;
}
private:
int m_Number = 10;
};
int main(int argc, char** argv)
{
// operator new
MTestClass* p = (MTestClass*)MTestClass::operator new(sizeof(MTestClass));
p->print();
// placement new
::new(p) MTestClass(50);
p->print();
// 执行析构函数
p->~MTestClass();
MTestClass::operator delete(p);
system("pause");
return 0;
}
程序的运行结果如下:
执行 operator new !
Number is : -842150451
执行构造函数 MTestClass(int num)
Number is : 50
执行析构函数
执行 operator delete !
根据运行结果可以看出,
::new(p) MTestClass(50);
我们经常说 new[] 和 delete[] 一定要成对出现,那么为什么呢?
其实是这样的,对于普通数据类型使用 new[] 创建的数组,使用 delete[] 和直接使用 delete 释放内存效果是一样的,对于自定义的类型,如果显示的声明了析构函数,则需要使用 delete[], 如果不是显示的声明析构函数,那么 delete 和 delete[] 的效果是相同的。
如果显示声明了析构函数,delete[] 需要调用n次析构函数,那么 delete[] 是怎么知道申请了多少个内存空间呢?我们可以看一下下面的伪代码
MTestClass *p = new MTestClass[10];
delete[] p;
分析如下:
首先 new MTestClass[10] 会申请 10 * sizeof(MTestClass) + sizeof(size_t) 个内存空间,为什么是 10 * sizeof(MTestClass) + sizeof(size_t) 个而不是 10 * sizeof(MTestClass) 个,是因为他要用 sizeof(size_t) 个字节存储数组的元素个数。 在调用 delete[] 的时候会根据第一个字节的个数去调用n次析构函数,然后释放掉整个内存空间。
为了验证上面的说法,我们将代码改写如下:
#include <iostream>
class MTestClass
{
public:
MTestClass(){
std::cout << "执行构造函数 MTestClass()" << std::endl;
}
MTestClass(int num) : m_Number(num) {
std::cout << "执行构造函数 MTestClass(int num)" << std::endl;
}
~MTestClass() {
std::cout << "执行析构函数" << std::endl;
}
void* operator new(size_t size) {
std::cout << "执行 operator new !" << std::endl;
return ::operator new(size);
}
void operator delete(void* pWhere) {
std::cout << "执行 operator delete !" << std::endl;
return ::operator delete(pWhere);
}
void* operator new[](size_t size) {
std::cout << "执行 operator new[] !" << "\tsize is " << size << std::endl;
return ::operator new[](size);
}
void operator delete[](void* pWhere){
std::cout << "执行 operator delete[] !" << std::endl;
return ::operator delete[](pWhere);
}
void print(void) {
std::cout << "Number is : " << m_Number << std::endl;
}
private:
int m_Number = 10;
int m_Number2 = 20;
};
int main(int argc, char** argv)
{
std::cout << sizeof(MTestClass) << ", " << sizeof(size_t) << std::endl;
MTestClass* p = new MTestClass[5];
delete[] p;
system("pause");
return 0;
}
运行结果为:
8, 4
执行 operator new[] ! size is 44
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行析构函数
执行析构函数
执行析构函数
执行析构函数
执行析构函数
执行 operator delete[] !
可以看出,operator new[] 申请的内存空间大小为44个字节,sizeof(MTestClass) 为8字节,而 sizeof(size_t) 为4字节,正好为 44 = 5 * 8 + 4,验证了申请空间大小为 n * sizeof(MTestClass) + sizeof(size_t)
如果我们不显示声明析构函数,又会怎么样呢??
去掉析构函数的完整代码如下:
#include <iostream>
class MTestClass
{
public:
MTestClass(){
std::cout << "执行构造函数 MTestClass()" << std::endl;
}
MTestClass(int num) : m_Number(num) {
std::cout << "执行构造函数 MTestClass(int num)" << std::endl;
}
void* operator new(size_t size) {
std::cout << "执行 operator new !" << std::endl;
return ::operator new(size);
}
void operator delete(void* pWhere) {
std::cout << "执行 operator delete !" << std::endl;
return ::operator delete(pWhere);
}
void* operator new[](size_t size) {
std::cout << "执行 operator new[] !" << "\tsize is " << size << std::endl;
return ::operator new[](size);
}
void operator delete[](void* pWhere){
std::cout << "执行 operator delete[] !" << std::endl;
return ::operator delete[](pWhere);
}
void print(void) {
std::cout << "Number is : " << m_Number << std::endl;
}
private:
int m_Number = 10;
int m_Number2 = 20;
};
int main(int argc, char** argv)
{
std::cout << sizeof(MTestClass) << ", " << sizeof(size_t) << std::endl;
MTestClass* p = new MTestClass[5];
delete[] p;
system("pause");
return 0;
}
程序运行结果:
8, 4
执行 operator new[] ! size is 40
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行构造函数 MTestClass()
执行 operator delete[] !
从结果可以看出,申请的大小为40,验证了如果没有显示的声明析构函数,则会申请 sizeof(MTestClass) * n 个字节的内存空间,这时使用delete 和使用 delete[] 效果是相同的。
通过上面这两个例子也可以看出
operator new 和 operator new[] 的效果是相同的,都是申请 size 大小的空间;
operator delete 和 operator delete[] 的效果也是相同的,都是释放堆内存空间。