new
当使用new的时候,实际上调用的是operator new,operator new在<new>
头文件中:
//@{
/** These are replaceable signatures:
* - normal single new and delete (no arguments, throw @c bad_alloc on error)
* - normal array new and delete (same)
* - @c nothrow single new and delete (take a @c nothrow argument, return
* @c NULL on error)
* - @c nothrow array new and delete (same)
*
* Placement new and delete signatures (take a memory address argument,
* does nothing) may not be replaced by a user's program.
*/
void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
__attribute__((__externally_visible__));
void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)
__attribute__((__externally_visible__));
void operator delete(void*) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void operator delete[](void*) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void operator delete(void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
void operator delete[](void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
__attribute__((__externally_visible__));
// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
// Default placement versions of operator delete.
inline void operator delete (void*, void*) _GLIBCXX_USE_NOEXCEPT { }
inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { }
//@}
new具体操作:
//头文件先忽略
using std::new_handler;
using std::bad_alloc;// using 声明
#if _GLIBCXX_HOSTED// 如果 有这个宏使用 std的malloc
using std::malloc;
#else// 没有则使用c语言的malloc
// A freestanding C runtime may not provide "malloc" -- but there is no
// other reasonable way to implement "operator new".
extern "C" void *malloc (std::size_t);
#endif
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
void *p;
// new_handler 以后说明,但是可以看出首先我们根据入口参数 sz的大小分配内存,
// 如果sz为0 则令其为1 ,然后在while循环中调用malloc申请内存
// 直到 申请成功 或者 抛出异常或者 abort
/* malloc (0) is unpredictable; avoid it. */
if (sz == 0)
sz = 1;
while (__builtin_expect ((p = malloc (sz)) == 0, false))
{
new_handler handler = std::get_new_handler ();
if (! handler)
_GLIBCXX_THROW_OR_ABORT(bad_alloc());
handler ();
}
return p;
}
其实在new的实现中都是使用一个while循环去一直malloc数据,如果malloc失败的话,则调用_callnewh
或者get_new_handler
。侯捷说_callnewh
是一种你可以设定的函数,那么应该在_callnewh
中释放内存以便下次调用malloc的时候可以成功。
new出的指针与构造函数
使用new出的指针调用构造函数在某些编译器上是可以的,在某些编译器(gnu c)是不可以的。
但是使用new出的指针调用析构函数是可以的,只不过在调用delete的时候会再次调用析构函数。
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "ctor" << endl; };
~A() { cout << "dtor" << endl; };
private:
int a;
};
int main()
{
A* p = new A();
// p->A();
p->~A();
delete p;
return 0;
}
输出为:
delete
delete内部直接使用free.
array new/array delete
在delete new出来的数组的时候,如果错误使用了delete p
而不是使用delete [] p;
可能会引起内存泄漏,对没有指针变量的对象可能没有影响,对于有指针变量的对象就会产生内存泄漏。
#include <iostream>
using namespace std;
class A {
public:
int id;
A() { cout << "default ctor.this=" << this << " id=" << id << endl; };
A(int _id) : id(_id) { cout << "ctor.this=" << this << " id=" << id << endl; };
~A() { cout << "dtor.this=" << this << " id=" << id << endl; };
};
int main()
{
int size = 3;
A* buf = new A[size];//調用3次構造函數, 0先于1先于2
A* tmp = buf;
cout << "buf=" << buf << " tmp=" << tmp << endl;
for(int i = 0; i < size; i++)
new(tmp++)A(i);//placement new
cout << "buf=" << buf << " tmp=" << tmp << endl;
delete[] buf;//dtor調用3次, 先析構2再析構1最後析構0
return 0;
}
输出:
default ctor.this=0x866d84 id=8782016
default ctor.this=0x866d88 id=0
default ctor.this=0x866d8c id=0
buf=0x866d84 tmp=0x866d84
ctor.this=0x866d84 id=0
ctor.this=0x866d88 id=1
ctor.this=0x866d8c id=2
buf=0x866d84 tmp=0x866d90
dtor.this=0x866d8c id=2
dtor.this=0x866d88 id=1
dtor.this=0x866d84 id=0
可以看出,在析构的时候,析构顺序是从最后一个到第一个。
placement new
- placement new允许在已经分配好的空间上构造对象。
- 这个内存空间可以不是动态分配的。
- placement new不能被重载。
- 对于需要频繁new delete的情景,如果只是简单地调用new delete不仅会比较慢(寻找合适的内存块需要时间),还会造成内存碎片。我们可以事先分配好内存,每次在这片内存上使用placement new构造出对象。
使用方法:
// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
第二个参数是一个指针,指向已经分配好的内存,然后直接在这片内存上通过new构造对象,它直接将传入的指针返回。
上图可以看出placement new被编译器解释为3步,首先调用operator new函数,然后强制类型转换成对象类型的指针,最后一步调用构造函数。
重载::operator new和::operator delete
全局的operator new
以及operator delete
是可以重载的,但一般不进行重载,因为这会影响到很多地方。
void* myAlloc(size_t size)
{ return malloc(size); }
void* myFree(void* ptr)
{ return free(ptr); }
inline void* operator new(size_t size)
{
cout << "my operator new" << endl;
return myAlloc(size);
}
inline void* operator new[](size_t size)
{
cout << "my operator new[]" << endl;
return myAlloc(size);
}
inline void* operator delete(void* ptr)
{
cout << "my operator delete" << endl;
return myFree(ptr);
}
inline void* operator delete[](void* ptr)
{
cout << "my operator delete[]" << endl;
return myFree(ptr);
}
在类中重载operator new与operator delete
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
class Foo
{
public:
int _id;
// long _data;
public:
Foo() : _id(0) {
cout << "default ctor.this=" << this << " id=" << _id << endl;
};
Foo(int i) : _id(i) {
cout << "ctor.this=" << this << " id=" << _id << endl;
};
~Foo() { cout << "dtor.this=" << this << " id=" << _id << endl; };
//这里必须是static
static void* operator new(size_t size);
static void operator delete(void* pdead, size_t size);
static void* operator new[](size_t size);
static void operator delete[](void* pdead, size_t size);
};
void* Foo::operator new(size_t size){
Foo* p = (Foo*)malloc(size);
cout << "Foo operator new, size=" << size <<"\treturn " << p << endl;
return p;
}
void Foo::operator delete(void* pdead, size_t size){
cout << "Foo operator delete, pdead=" << pdead << " size=" << size << endl;
free(pdead);
}
void* Foo::operator new[](size_t size){
Foo* p = (Foo*)malloc(size);
cout << "Foo operator new[], size=" << size <<"\treturn " << p << endl;
return p;
}
void Foo::operator delete[](void* pdead, size_t size){
cout << "Foo operator delete[], pdead=" << pdead << " size=" << size << endl;
free(pdead);
}
int main()
{
Foo* p = new Foo(7);
delete p;
Foo* pArray = new Foo[5];
delete [] pArray;
return 0;
}
注意在类中重载的operator new等函数必须是static,因为如果自己在类中重载了operator new,那么分配内存时就会调用自己重载的那个new。所以如果new不是static的,调用new的时候还没有对象产生,又该如何new出对象呢?将new声明为static就可以在没有对象的时候还能调用自己重载的operator new了,因为静态成员函数不需要对象生成就可以调用。当将运算符函数定义为类的成员时,他们是隐式静态的,无需显式地声明static。
上述代码的输出为:
Foo operator new, size=4 return 0xeca040
ctor.this=0xeca040 id=7
dtor.this=0xeca040 id=7
Foo operator delete, pdead=0xeca040 size=4
Foo operator new[], size=24 return 0xec0578
default ctor.this=0xec057c id=0
default ctor.this=0xec0580 id=0
default ctor.this=0xec0584 id=0
default ctor.this=0xec0588 id=0
default ctor.this=0xec058c id=0
dtor.this=0xec058c id=0
dtor.this=0xec0588 id=0
dtor.this=0xec0584 id=0
dtor.this=0xec0580 id=0
dtor.this=0xec057c id=0
Foo operator delete[], pdead=0xec0578 size=24
可以看出,如果自己在类中重载了operator new,那么会绕过全局的operator new,自动调用自己重载的operator new。当然也可以在new前面加上::表示使用全局的new而不是自己定义的new。