當(dāng) operator new 不能滿足一個(gè)內(nèi)存分配請(qǐng)求時(shí),它拋出一個(gè) exception(異常)。很久以前,他返回一個(gè) null pointer(空指針),而一些比較老的編譯器還在這樣做。你依然能達(dá)到以前的目的(在一定程度上),但是我要到本文的最后再討論它。
在 operator new 因回應(yīng)一個(gè)無法滿足的內(nèi)存請(qǐng)求而拋出一個(gè) exception 之前,它先調(diào)用一個(gè)可以由客戶指定的被稱為 new-handler 的 error-handling function(錯(cuò)誤處理函數(shù))。(這并不完全確切,operator new 真正做的事情比這個(gè)稍微復(fù)雜一些,詳細(xì)細(xì)節(jié)將在下一篇文章中討論。)為了指定 out-of-memory-handling function,客戶調(diào)用 set_new_handler ——一個(gè)在 中聲明的標(biāo)準(zhǔn)庫(kù)函數(shù):
namespace std {
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}
就像你能夠看到的,new_handler 是一個(gè)指針的 typedef,這個(gè)指針指向不取得和返回任何東西的函數(shù),而 set_new_handler 是一個(gè)取得和返回一個(gè) new_handler 的函數(shù)。(set_new_handler 的聲明的結(jié)尾處的 "throw()" 是一個(gè) exception specification(異常規(guī)范)。它基本上是說這個(gè)函數(shù)不會(huì)拋出任何異常,盡管真相更有趣一些。關(guān)于細(xì)節(jié),參見《C++箴言:爭(zhēng)取異常安全的代碼》。)
set_new_handler 的形參是一個(gè)指向函數(shù)的指針,這個(gè)函數(shù)是 operator new 無法分配被請(qǐng)求的內(nèi)存時(shí)應(yīng)該調(diào)用的。set_new_handler 的返回值是一個(gè)指向函數(shù)的指針,這個(gè)函數(shù)是 set_new_handler 被調(diào)用前有效的目標(biāo)。
你可以像這樣使用 set_new_handler:
// function to call if operator new can't allocate enough memory
void outOfMem()
{
std::cerr << "Unable to satisfy request for memory\n";
std::abort();
}
int main()
{
std::set_new_handler(outOfMem);
int *PBigDataArray = new int[100000000L];
...
}
如果 operator new 不能為 100,000,000 個(gè)整數(shù)分配空間,outOfMem 將被調(diào)用,而程序?qū)⒃诎l(fā)出一個(gè)錯(cuò)誤信息后中止。(順便說一句,考慮如果在寫這個(gè)錯(cuò)誤信息到 cerr... 的過程中內(nèi)存必須被動(dòng)態(tài)分配會(huì)發(fā)生什么。)
當(dāng) operator new 不能滿足一個(gè)內(nèi)存請(qǐng)求時(shí),它反復(fù)調(diào)用 new-handler function 直到它能找到足夠的內(nèi)存。但是從這種高層次的描述已足夠推導(dǎo)出一個(gè)設(shè)計(jì)得好的 new-handler function 必須做到以下事情之一:
·Make more memory available(使得更多的內(nèi)存可用)。這可能使得 operator new 中下一次內(nèi)存分配的嘗試成功。實(shí)現(xiàn)這一策略的一個(gè)方法是在程序啟動(dòng)時(shí)分配一大塊內(nèi)存,然后在 new-handler 第一次被調(diào)用時(shí)釋放它供程序使用。
·Install a different new-handler(安裝一個(gè)不同的 new-handler)。如果當(dāng)前的 new-handler 不能做到使更多的內(nèi)存可用,或許它知道有一個(gè)不同的 new-handler 可以做到。如果是這樣,當(dāng)前的 new-handler 能在它自己的位置上安裝另一個(gè) new-handler(通過調(diào)用 set_new_handler)。operator new 下一次調(diào)用 new-handler function 時(shí),它會(huì)得到最近安裝的那一個(gè)。(這個(gè)主線上的一個(gè)變化是讓一個(gè) new-handler 改變它自己的行為,這樣,下一次它被調(diào)用時(shí),可以做一些不同的事情。做到這一點(diǎn)的一個(gè)方法是讓 new-handler 改變能影響 new-handler 行為的 static(靜態(tài)),namespace-specific(名字空間專用)或 global(全局)的數(shù)據(jù)。)
·Deinstall the new-handler(卸載 new-handler),也就是,將空指針傳給 set_new_handler。沒有 new-handler 被安裝,當(dāng)內(nèi)存分配沒有成功時(shí),operator new 拋出一個(gè)異常。
·Throw an exception(拋出一個(gè)異常),類型為 bad_alloc 或繼承自 bad_alloc 的其它類型。這樣的異常不會(huì)被 operator new 捕獲,所以它們將被傳播到發(fā)出內(nèi)存請(qǐng)求的地方。
·Not return(不再返回),典型情況下,調(diào)用 abort 或 exit。
這些選擇使你在實(shí)現(xiàn) new-handler functions 時(shí)擁有極大的彈性。
有時(shí)你可能希望根據(jù)被分配 object 的不同,用不同的方法處理內(nèi)存分配的失?。?BR> class X {
public:
static void outOfMemory();
...
};
class Y {
public:
static void outOfMemory();
...
};
X* p1 = new X; // if allocation is unsuccessful,
// call X::outOfMemory
Y* p2 = new Y; // if allocation is unsuccessful,
// call Y::outOfMemory
C++ 沒有對(duì) class-specific new-handlers 的支持,但是它也不需要。你可以自己實(shí)現(xiàn)這一行為。你只要讓每一個(gè) class 提供 set_new_handler 和 operator new 的它自己的版本即可。class 的 set_new_handler 允許客戶為這個(gè) class 指定 new-handler(正像standard set_new_handler 允許客戶指定global new-handler)。class 的 operator new 確保當(dāng)為 class objects 分配內(nèi)存時(shí),class-specific new-handler 代替 global new-handler 被使用。
假設(shè)你要為 Widget class 處理內(nèi)存分配失敗。你就必須清楚當(dāng) operator new 不能為一個(gè) Widget object 分配足夠的內(nèi)存時(shí)所調(diào)用的函數(shù),所以你需要聲明一個(gè) new_handler 類型的 static member(靜態(tài)成員)指向這個(gè) class 的 new-handler function。Widget 看起來就像這樣:
class Widget {
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void * operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
static class members(靜態(tài)類成員)必須在 class 定義外被定義(除非它們是 const 而且是 integral),所以:
std::new_handler Widget::currentHandler = 0; // init to null in the class
// impl. file
Widget 中的 set_new_handler 函數(shù)會(huì)保存?zhèn)鬟f給它的任何指針,而且會(huì)返回前次調(diào)用時(shí)被保存的任何指針,這也正是 set_new_handler 的標(biāo)準(zhǔn)版本所做的事情:
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
在 operator new 因回應(yīng)一個(gè)無法滿足的內(nèi)存請(qǐng)求而拋出一個(gè) exception 之前,它先調(diào)用一個(gè)可以由客戶指定的被稱為 new-handler 的 error-handling function(錯(cuò)誤處理函數(shù))。(這并不完全確切,operator new 真正做的事情比這個(gè)稍微復(fù)雜一些,詳細(xì)細(xì)節(jié)將在下一篇文章中討論。)為了指定 out-of-memory-handling function,客戶調(diào)用 set_new_handler ——一個(gè)在
namespace std {
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}
就像你能夠看到的,new_handler 是一個(gè)指針的 typedef,這個(gè)指針指向不取得和返回任何東西的函數(shù),而 set_new_handler 是一個(gè)取得和返回一個(gè) new_handler 的函數(shù)。(set_new_handler 的聲明的結(jié)尾處的 "throw()" 是一個(gè) exception specification(異常規(guī)范)。它基本上是說這個(gè)函數(shù)不會(huì)拋出任何異常,盡管真相更有趣一些。關(guān)于細(xì)節(jié),參見《C++箴言:爭(zhēng)取異常安全的代碼》。)
set_new_handler 的形參是一個(gè)指向函數(shù)的指針,這個(gè)函數(shù)是 operator new 無法分配被請(qǐng)求的內(nèi)存時(shí)應(yīng)該調(diào)用的。set_new_handler 的返回值是一個(gè)指向函數(shù)的指針,這個(gè)函數(shù)是 set_new_handler 被調(diào)用前有效的目標(biāo)。
你可以像這樣使用 set_new_handler:
// function to call if operator new can't allocate enough memory
void outOfMem()
{
std::cerr << "Unable to satisfy request for memory\n";
std::abort();
}
int main()
{
std::set_new_handler(outOfMem);
int *PBigDataArray = new int[100000000L];
...
}
如果 operator new 不能為 100,000,000 個(gè)整數(shù)分配空間,outOfMem 將被調(diào)用,而程序?qū)⒃诎l(fā)出一個(gè)錯(cuò)誤信息后中止。(順便說一句,考慮如果在寫這個(gè)錯(cuò)誤信息到 cerr... 的過程中內(nèi)存必須被動(dòng)態(tài)分配會(huì)發(fā)生什么。)
當(dāng) operator new 不能滿足一個(gè)內(nèi)存請(qǐng)求時(shí),它反復(fù)調(diào)用 new-handler function 直到它能找到足夠的內(nèi)存。但是從這種高層次的描述已足夠推導(dǎo)出一個(gè)設(shè)計(jì)得好的 new-handler function 必須做到以下事情之一:
·Make more memory available(使得更多的內(nèi)存可用)。這可能使得 operator new 中下一次內(nèi)存分配的嘗試成功。實(shí)現(xiàn)這一策略的一個(gè)方法是在程序啟動(dòng)時(shí)分配一大塊內(nèi)存,然后在 new-handler 第一次被調(diào)用時(shí)釋放它供程序使用。
·Install a different new-handler(安裝一個(gè)不同的 new-handler)。如果當(dāng)前的 new-handler 不能做到使更多的內(nèi)存可用,或許它知道有一個(gè)不同的 new-handler 可以做到。如果是這樣,當(dāng)前的 new-handler 能在它自己的位置上安裝另一個(gè) new-handler(通過調(diào)用 set_new_handler)。operator new 下一次調(diào)用 new-handler function 時(shí),它會(huì)得到最近安裝的那一個(gè)。(這個(gè)主線上的一個(gè)變化是讓一個(gè) new-handler 改變它自己的行為,這樣,下一次它被調(diào)用時(shí),可以做一些不同的事情。做到這一點(diǎn)的一個(gè)方法是讓 new-handler 改變能影響 new-handler 行為的 static(靜態(tài)),namespace-specific(名字空間專用)或 global(全局)的數(shù)據(jù)。)
·Deinstall the new-handler(卸載 new-handler),也就是,將空指針傳給 set_new_handler。沒有 new-handler 被安裝,當(dāng)內(nèi)存分配沒有成功時(shí),operator new 拋出一個(gè)異常。
·Throw an exception(拋出一個(gè)異常),類型為 bad_alloc 或繼承自 bad_alloc 的其它類型。這樣的異常不會(huì)被 operator new 捕獲,所以它們將被傳播到發(fā)出內(nèi)存請(qǐng)求的地方。
·Not return(不再返回),典型情況下,調(diào)用 abort 或 exit。
這些選擇使你在實(shí)現(xiàn) new-handler functions 時(shí)擁有極大的彈性。
有時(shí)你可能希望根據(jù)被分配 object 的不同,用不同的方法處理內(nèi)存分配的失?。?BR> class X {
public:
static void outOfMemory();
...
};
class Y {
public:
static void outOfMemory();
...
};
X* p1 = new X; // if allocation is unsuccessful,
// call X::outOfMemory
Y* p2 = new Y; // if allocation is unsuccessful,
// call Y::outOfMemory
C++ 沒有對(duì) class-specific new-handlers 的支持,但是它也不需要。你可以自己實(shí)現(xiàn)這一行為。你只要讓每一個(gè) class 提供 set_new_handler 和 operator new 的它自己的版本即可。class 的 set_new_handler 允許客戶為這個(gè) class 指定 new-handler(正像standard set_new_handler 允許客戶指定global new-handler)。class 的 operator new 確保當(dāng)為 class objects 分配內(nèi)存時(shí),class-specific new-handler 代替 global new-handler 被使用。
假設(shè)你要為 Widget class 處理內(nèi)存分配失敗。你就必須清楚當(dāng) operator new 不能為一個(gè) Widget object 分配足夠的內(nèi)存時(shí)所調(diào)用的函數(shù),所以你需要聲明一個(gè) new_handler 類型的 static member(靜態(tài)成員)指向這個(gè) class 的 new-handler function。Widget 看起來就像這樣:
class Widget {
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void * operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
static class members(靜態(tài)類成員)必須在 class 定義外被定義(除非它們是 const 而且是 integral),所以:
std::new_handler Widget::currentHandler = 0; // init to null in the class
// impl. file
Widget 中的 set_new_handler 函數(shù)會(huì)保存?zhèn)鬟f給它的任何指針,而且會(huì)返回前次調(diào)用時(shí)被保存的任何指針,這也正是 set_new_handler 的標(biāo)準(zhǔn)版本所做的事情:
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}