new和delete是C++用于内存管理的操作符,对应与C中的malloc和free,他们必须成对使用,new的内存不能调用free来释放,两者的区别:
1、malloc只分配内存,接收size_t类型参数,指定要分配内存大小,返回void*指针,free只负责释放内存,而new接收参数为类型(new[]参数为类型和数组大小),除了分配内存还会调用类的构造函数(如果new的对象为类的话),返回对应类型的指针,delete会先调用类的析构函数再释放内存。
2、malloc分配失败时返回空指针,new分配失败时抛出异常std::bad_alloc,如果通过set_new_handler设置了失败处理函数则会调用该函数,或者向new传入了std::nothrow参数,则返回空指针。
class A
{
public:
A()
{
data = 0;
std::cout << "A() called" << std::endl;
}
virtual ~A()
{
std::cout << "~A() called" << std::endl;
}
int data;
};
int main()
{
std::cout << "test malloc:" << std::endl;
A* pMalloc = (A*)malloc(sizeof(A));
if (pMalloc)
{
std::cout << pMalloc->data<<std::endl;
free(pMalloc);
}
std::cout << "test new:" << std::endl;
A* pNew = new A;
std::cout << pNew->data << std::endl;
delete pNew;
system("pause");
return 0;
}
另一个很容易混淆的是new/delete和operator new/operator delete,new操作可分为两步
(1)调用了operator new,分配需要的字节数的内存,返回void*指针,这里分配内存调用malloc;
(2)调用类构造函数,返回对应类型的指针。delete则是调用析构函数并释放内存(调用free)。
operator new和operator delete可以通过重载实现自己的分配方式。如下示例,这是在32位编译器下运行,所以类A所占内存大小等于data的4字节+虚函数表指针的4字节(析构函数定义为虚函数),一共是8字节(如果是64位编译器由于字节对齐的机制将会是16字节),也就是operator new和operator delete传入的参数size=8,operator delete用不到这个size参数,系统能知道需要释放的内存大小。
class A
{
public:
A()
{
data = 0;
std::cout << "A() called" << std::endl;
}
virtual ~A()
{
std::cout << "~A() called" << std::endl;
}
void* operator new(size_t size)
{
std::cout << "operator new called, size=" <<size<< std::endl;
return malloc(size);
//return ::operator new(size);//也可以调用全局的::operator new
}
void operator delete(void* ptr,size_t size)//size参数可有可无
{
std::cout << "operator delete called, size="<<size << std::endl;
free(ptr);
//::operator delete(ptr);//也可以调用全局的::operator delete
}
int data;
};
int main()
{
A* pNew = new A;
std::cout << pNew->data << std::endl;
delete pNew;
system("pause");
return 0;
}
重载operator new和operator delete在某些场合很有用,比如游戏开发或其他对程序性能要求比较高的场合,可以在程序启动时分配一块大的内存,然后在调用new的时候在这块大内存上分配需要的存储空间,调用delete时释放掉。
下面用一个简单的示例说明一下这种分配方式,当然这里的代码还不能真正的使用,还有很多未考虑的地方,比如保存多个所占内存大小的不一样的对象时可能就会出错,仅仅展示下重载operator new和operator delete的一种用法。为了方便查看数据析构函数不定义为虚函数,类中的数据就只有data。
char* g_buffer = nullptr;//程序启动时分配的总内存空间
int g_bufferSize = 0;//总内存空间大小
class A
{
public:
A()
{
data = 0x12345678;
std::cout << "A() called" << std::endl;
}
~A()
{
std::cout << "~A() called" << std::endl;
}
void* operator new(size_t size)
{
std::cout << "operator new called, size=" <<size<< std::endl;
if (g_buffer)
{
char* pLocation = g_buffer;
int bufSize = *((int*)pLocation);
while (bufSize != 0)//查找未分配的内存,bufSize为当前已分配内存的大小
{
pLocation += sizeof(int)+bufSize;//如果该位置已分配,跳到这块内存的结尾继续查找下一块
if (pLocation - g_buffer > g_bufferSize-size-sizeof(int))
{
//没有合适的内存,返回空指针
pLocation = nullptr;
break;
}
bufSize = *((int*)pLocation);
}
if (pLocation)
{
*((int*)pLocation) = size;//在开始位置之前存放需要分配的字节数的值
pLocation += sizeof(int);//移到真正需要存放数据的位置
}
return pLocation;
}
return malloc(size);
}
void operator delete(void* ptr,size_t size)
{
std::cout << "operator delete called, size="<<size << std::endl;
if (g_buffer)
{
char* pLocation = (char*)ptr;
if (pLocation - g_buffer >= sizeof(int))
{
*((int*)(pLocation - sizeof(int))) = 0;//将内存大小部分设置为0,表示当前内存没有被使用
}
return;
}
free(ptr);
}
int data;
};
int main()
{
g_buffer = new char[1024];
std::cout << "g_buffer address=0x" << (void*)g_buffer << std::endl;
memset(g_buffer,0,1024);
g_bufferSize = 1024;
A* pA1 = new A;
std::cout << std::hex;
std::cout << "-------------pA1 view begin------------" << std::endl;
std::cout << "pA1 address=0x" <<(void*)pA1<<" data=0x"<<pA1->data << std::endl;
for (int i = 0; i < 16; i++)
{
std::cout << (int)g_buffer[i] << ",";
}
std::cout<<std::endl<<"-------------pA1 view end--------------"<<std::endl;
std::cout << std::dec;
A* pA2 = new A;
pA2->data = 0x11223344;
std::cout << std::hex;
std::cout << "-------------pA2 view begin------------" << std::endl;
std::cout << "pA2 address=0x" << (void*)pA2 << " data=0x" << pA2->data << std::endl;
for (int i = 0; i < 16; i++)
{
std::cout << (int)g_buffer[i] << ",";
}
std::cout <<std::endl<<"-------------pA2 view end--------------"<< std::endl;
std::cout << std::dec;
delete pA1;
delete pA2;
delete[] g_buffer;
system("pause");
return 0;
}
运行结果如下图
operator new除了第一个参数必须是size_t外,后面可以加任意的参数,如void* operator new(size_t size,const char* info);调用的时候就是这样A* pA = new("mytest") A;,同样operator delete也可以加上其他参数。
重载的operator new和operator delete是静态函数,不可以使用成员函数或变量,因为在operator new分配内存之前对象都还不存在,也不可能使用任何对象实例的数据,operator delete是析构函数函数之后才调用,也不可能使用对象实例数据。
new[]和delete[]是针对数组的,用法类似。