C++ 内存分配(new,operator new)面面观
本文主要讲述C++ new运算符和operator new, placement new之间的种种关联,new的底层实现,以及operator new的重载和一些在内存池,STL中的应用。
一 new运算符和operator new():
new:指我们在C++里通常用到的运算符,比如A* a = new A; 对于new来说,有new和::new之分,前者位于std
operator new():指对new的重载形式,它是一个函数,并不是运算符。对于operator new来说,分为全局重载和类重载,全局重载是void* ::operator new(size_t size),在类中重载形式 void* A::operator new(size_t size)。还要注意的是这里的operator new()完成的操作一般只是分配内存,事实上系统默认的全局::operator new(size_t size)也只是调用malloc分配内存,并且返回一个void*指针。而构造函数的调用(如果需要)是在new运算符中完成的。
先简单解释一下new和operator new之间的关系:
operator new can be called explicitly as a regular function, but in C++, new is an operator with a very specific
behavior: An expression with the new operator, first calls function operator new (i.e., this function) with the size of its type specifier as first argument, and if this is successful, it then automatically initializes or constructs
the object (if needed). Finally, the expression evaluates as a pointer to the appropriate type.
比如我们写如下代码:
A* a = new A;
我们知道这里分为两步:1.分配内存,2.调用A()构造对象。事实上,分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),如果没有重载,就调用::operator new(size_t ),全局new操作符由C++默认提供。因此前面的两步也就是:1.调用operator new 2.调用构造函数。这里再一次提出来是因为后面关于这两步会有一些变形,在关于placement new那里会讲到。先举个简单例子
[cpp]
<SPAN style="FONT-FAMILY: SimHei; FONT-SIZE: 14px">//平台:Visual Stdio 2008
#include<iostream>
class A
{
public:
A()
{
std::cout<<"call A constructor"<<std::endl;
}
~A()
{
std::cout<<"call A destructor"<<std::endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
A* a = new A;
delete a;
system("pause");
return 0;
}</SPAN>
//平台:Visual Stdio 2008
#include<iostream>
class A
{
public:
A()
{
std::cout<<"call A constructor"<<std::endl;
}
~A()
{
std::cout<<"call A destructor"<<std::endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
A* a = new A;
delete a;
system("pause");
return 0;
}下面我们跟踪一下A反汇编代码,由于Debug版本反汇编跳转太多,因此此处通过Release版本在A* a = new A;处设断点反汇编:
在Release版本中,构造函数和析构函数都是直接展开的。
[plain]
<SPAN style="FONT-FAMILY: SimHei; FONT-SIZE: 14px"> A* a = new A;
01301022 push 1 ;不含数据成员的类占用一字节空间,此处压入sizeof(A)
01301024 call operator new (13013C2h) ;调用operator new(size_t size)
01301029 mov esi,eax ;返回值保存到esi
0130102B add esp,4 ;平衡栈
0130102E mov dword ptr [esp+8],esi ;
01301032 mov dword ptr [esp+14h],0
0130103A test esi,esi ;在operator new之后,检查其返回值,如果为空(分配失败),则不调用A()构造函数
0130103C je wmain+62h (1301062h) ;为空 跳过构造函数部分
0130103E mov eax,dword ptr [__imp_std::endl (1302038h)] ;构造函数内部,输出字符串
01301043 mov ecx,dword ptr [__imp_std::cout (1302050h)]
01301049 push eax
0130104A push offset string "call A constructor" (1302134h)
0130104F push ecx
01301050 call std::operator<<<std::char_traits<char> > (13011F0h)
01301055 add esp,8
01301058 mov ecx,eax
0130105A call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (1302040h)]
01301060 jmp wmain+64h (1301064h) ;构造完成,跳过下一句
01301062 xor esi,esi ;将esi置空,这里的esi即为new A的返回值
01301064 mov dword ptr [esp+14h],0FFFFFFFFh
delete a;
0130106C test esi,esi ;检查a是否为空
0130106E je wmain+9Bh (130109Bh) ;如果为空,跳过析构函数和operator delete
01301070 mov edx,dword ptr [__imp_std::endl (1302038h)] ;析构函数 输出字符串
01301076 mov eax,dword ptr [__imp_std::cout (1302050h)]
0130107B push edx
0130107C push offset string "call A destructor" (1302148h)
01301081 push eax
01301082 call std::operator<<<std::char_traits<char> > (13011F0h)
01301087 add esp,8
0130108A mov ecx,eax
0130108C call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (1302040h)]
01301092 push esi ;压入a
01301093 call&nb
补充:软件开发 , C++ ,