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。