1. 程式人生 > >操作符過載之new與delete

操作符過載之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;
}