操作符過載之new與delete
之前我是寡聞了,最近讀書時,發現一個圖表,上面的內容中發現new與delete是可以過載的,真的是出乎意料!居然還有這種操作,然後我在網上查了一下,看到幾個簡單的示例,作為學習方式,寫下這篇部落格鞏固一下。
沒錯,new與delete都是一種操作符,當我們在程式碼中new一個物件時,分為兩步,首先是operator new的運作,就像C語言中的malloc一樣,負責分配記憶體,然後是placement new的運作,負責給分配到的記憶體初始化(當我們給某個類過載new時,就是這個placement new呼叫的類建構函式)。其中前面那個負責分配記憶體的步驟,才是我們可以過載的new步驟,至於為什麼要把new、delete設計成可以過載的樣子,估計是為了提高C++的靈活性吧,比如我們自己編寫的new和delete,可以加入一個統計記憶體申請次數和記憶體釋放次數等。
new過載的方式有兩種,第一種是隱藏式過載,意思就是把本來提供給我們的new操作符版本給隱藏掉(這是我個人的猜測),這就意味著用這個方式過載new時,必須且只能給函式提供一個unsigned int型引數(size_t),因為正式的new版本有一個size_t型別引數,這個引數代表要申請的記憶體位元組的大小;第二種方式是改變引數特徵標,給operator new增加其他的引數,但是第一個引數必須得是size_t型別!
首先我們來看第一種型別的operator new過載示例:
#include <iostream> using namespace std; int i = 0; void* operator new(size_t a) { cout << "這是非常荒唐且危險的程式!" << endl; return &i; } int main() { cout << "i的值是: " << i << endl; int *p = new int; *p = 9999; cout << "現在i的值卻被意外改變了: " << i << endl; return 0; }
執行結果會是:
【i的值是:0】
【這是非常荒唐且危險的程式!】
【現在i的值被意外改變了:9999】
正像示例中說的那樣,這樣的程式碼很不負責任!正確的做法應該是配合庫函式malloc使用,但我想為了更好的詮釋operator new過載的靈活性,所以還是寫了這種爛程式碼來做說明。但在下面的程式碼示例中,我會將功補過,使用一種相對正確的做法來詮釋operator new的多引數過載版本:
#include <iostream> using namespace std; int i = 1;//用來統計我們的new使用了多少次 void* operator new(size_t a,int b) { cout << "這是我們第" <<i<<"次使用我們自己的new操作符"<< endl; i++; return malloc(a); } int main() { int *p1 = new (i)int;//呼叫我們的new int *p2 = new int;//這個呼叫的是正式的new int *p3 = new (i)int;//呼叫我們的new return 0; }
值得特別說明的是,在我們過載的函式中,i對應的是b,int(內容為4)對應的才是a,如果讀者朋友有心,可以在函式中自己測試一下,這裡不再贅述。
以上兩種new的過載,都是屬於全域性new的過載,但事實上,我們並沒有過載全域性new與delete的理由,因為正式的new,要比我們自己定義的new合理的多!C++之所以允許new能夠被過載,主要是應付在我們自己的類上面,比如我們自己開發了一個類,如果在堆上給這個類開闢一個類物件時需要一些特殊的動作,那麼我們應該給這個類域過載一個我們的new操作符,這樣當我們new這個物件時會執行那些特殊的動作,下面就是程式碼,為了不文不對題,也有delete的過載示例(兩個過載的操作符只對本類有效,對派生類無效,如果有派生類的話):
#include <iostream>
using namespace std;
class A
{
public:
int a;
A() :a(0) {};
void * operator new(unsigned i)//new的過載示例
{
cout << "呼叫我們自己的new操作符" << endl;
if (i > sizeof(A))//如果呼叫此new的是派生類(大於A)
{
return ::operator new(i);//那麼呼叫全域性的正式new
}
return malloc(i);
}
void operator delete(void * p,size_t i)//第一個p代表要釋放的首地址指標
{
cout << "呼叫我們自己的delete操作符" << endl;
if (p)//檢測指標是否為空,以防刪除空指標報錯!
{
if (i != sizeof(A))
{
return ::operator delete(p);
}
free(p);
}
}
};
int main()
{
A * p(new (A));
delete p;
return 0;
}