C++ 使用 new 和 delete 運算子支援物件的動態配置與解除配置。 這些運算子會從稱為「可用儲存區」(也稱為「堆積」) 的集區配置物件的記憶體。
new 運算子會呼叫特殊函式 operator new,而 delete 運算子會呼叫特殊函式 operator delete。
如需 C 執行階段程式庫和 C++ 標準程式庫中的程式庫檔案清單,請參閱 CRT 程式庫功能。
new 運算子
編譯器會將此種陳述式轉譯成對函式 operator new 的呼叫:
char *pch = new char[BUFFER_SIZE];
如果要求為零位元組的儲存體,operator new 會傳回指向不同物件的指標。 也就是說,重複呼叫 operator new 會傳回不同的指標。
如果配置要求的記憶體不足,operator new 會擲回 std::bad_alloc 例外狀況。 或者,如果您已使用 nullptr 形式 ,或如果您已在非擲回 new(std::nothrow)支援中連結,則會傳回 operator new。 如需詳細資訊,請參閱配置失敗行為。
下表描述 operator new 函式的兩個範圍。
operator new 函式的範圍
| 運算子 | Scope |
|---|---|
::operator new |
全球 |
class-name::operator new |
類別 |
operator new 的第一個引數必須是 size_t 類型,而且傳回型別一律為 void*。
全域 operator new 函式是在 new 運算子用於配置內建類型的物件、不包含使用者定義的 operator new 函式之類別類型的物件,以及任何類型的陣列時呼叫。 當 new 運算子用來配置其中已定義 operator new 之類別類型的物件時,就會呼叫該類別的 operator new。
為類別定義的 operator new 函式是靜態成員函式 (不能是虛擬的),可隱藏該類別類型之物件的全域 operator new 函式。 假設有 new 用來將記憶體配置和設定為特定值的案例:
#include <malloc.h>
#include <memory.h>
class Blanks
{
public:
Blanks(){}
void *operator new( size_t stAllocateBlock, char chInit );
};
void *Blanks::operator new( size_t stAllocateBlock, char chInit )
{
void *pvTemp = malloc( stAllocateBlock );
if( pvTemp != 0 )
memset( pvTemp, chInit, stAllocateBlock );
return pvTemp;
}
// For discrete objects of type Blanks, the global operator new function
// is hidden. Therefore, the following code allocates an object of type
// Blanks and initializes it to 0xa5
int main()
{
Blanks *a5 = new(0xa5) Blanks;
return a5 != 0;
}
括號中提供給 new 的引數會傳遞至 Blanks::operator new 做為 chInit 引數。 不過,全域 operator new 函式已隱藏,因此造成下列程式碼產生錯誤:
Blanks *SomeBlanks = new Blanks;
編譯器支援類別宣告中的成員陣列 new 和 delete 運算子。 例如:
class MyClass
{
public:
void * operator new[] (size_t)
{
return 0;
}
void operator delete[] (void*)
{
}
};
int main()
{
MyClass *pMyClass = new MyClass[5];
delete [] pMyClass;
}
配置失敗行為
C++ 標準程式庫中的 new 函式支援自 C++98 以來在 C++ 標準中指定的行為。 當配置要求的記憶體不足時,operator new 會擲回 std::bad_alloc 例外狀況。
舊版 C++ 程式碼針對失敗配置傳回了 Null 指標。 如果您有程式碼預期有 new 的非擲回版本,請將程式與 nothrownew.obj 連結。
nothrownew.obj 檔案會以配置失敗時傳回 operator new 的版本取代全域 nullptr。
operator new 不再擲回 std::bad_alloc。 如需 nothrownew.obj 和其他連結器選項檔案的詳細資訊,請參閱連結選項。
您無法將檢查全域 operator new 例外狀況的程式碼與檢查相同應用程式中 Null 指標的程式碼混合在一起。 不過,您仍可建立以不同方式運作的 class-local operator new。 這種可能性表示編譯器預設必須採取防禦性動作,並在 new 呼叫中包含 null 指標傳回的檢查。 如需最佳化這些編譯器檢查的方式詳細資訊,請參閱 /Zc:throwingnew。
處理記憶體不足
從 new 運算式測試失敗配置的方式,取決於您使用的是標準例外狀況機制,還是使用 nullptr 傳回。 標準 C++ 預期配置器會擲回 std::bad_alloc 或有衍生自 std::bad_alloc 的類別。 您可以處理這類例外狀況,如此範例所示:
#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
try {
int *pI = new int[BIG_NUMBER];
}
catch (bad_alloc& ex) {
cout << "Caught bad_alloc: " << ex.what() << endl;
return -1;
}
}
當您使用 nothrow 的 new 形式時,您可測試配置失敗,如下列範例所示:
#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
int *pI = new(nothrow) int[BIG_NUMBER];
if ( pI == nullptr ) {
cout << "Insufficient memory" << endl;
return -1;
}
}
當您使用 nothrownew.obj 檔案來取代全域 operator new 時,您可測試失敗的記憶體配置,如下所示:
#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
int *pI = new int[BIG_NUMBER];
if ( !pI ) {
cout << "Insufficient memory" << endl;
return -1;
}
}
您可為失敗的記憶體配置要求提供處理常式。 可以撰寫自訂復原常式來處理這類失敗。 例如,它可以釋出一些保留的記憶體,然後允許配置再次執行。 如需詳細資訊,請參閱_set_new_handler。
delete 運算子
使用 new 運算子動態配置的記憶體可以使用 delete 運算子加以釋放。 delete 運算子會呼叫 operator delete 函式,該函式會將記憶體釋放回可用的集區。 使用 delete 運算子也會導致呼叫類別解構函式 (如果有的話)。
有全域和類別範圍的 operator delete 函式。 針對指定的類別只能定義一個 operator delete 函式;如果已定義,則會隱藏全域 operator delete 函式。 一律會針對任何類型的陣列呼叫全域 operator delete 函式。
全域 operator delete 函式。 全域 operator delete 和類別成員 operator delete 函式有兩種形式:
void operator delete( void * );
void operator delete( void *, size_t );
特定類別只能存在前兩個形式的其中一個。 第一種形式採用類型為 void * 的單一引數,其中包含要解除配置之物件的指標。 第二種形式 (依大小調整的解除配置) 接受兩個引數,第一個是解除配置記憶體區塊的指標,第二個是要解除配置的位元組數目。 這兩種形式的傳回類型是 void (operator delete 無法傳回值)。
第二種形式的意圖是加快搜尋要刪除之物件的正確大小類別。 此資訊通常不會儲存在配置本身附近,而且可能未快取。 當基底類別中的 operator delete 函式用來刪除衍生類別的物件時,第二種形式就很有用。
operator delete 函式是靜態的,因此不能是虛擬函式。
operator delete 函式會遵守存取控制項,如成員存取控制中所述。
下列範例說明使用者定義的 operator new 和 operator delete 函式,這兩個函式是針對記錄記憶體的配置和解除配置所設計:
#include <iostream>
using namespace std;
int fLogMemory = 0; // Perform logging (0=no; nonzero=yes)?
int cBlocksAllocated = 0; // Count of blocks allocated.
// User-defined operator new.
void *operator new( size_t stAllocateBlock ) {
static int fInOpNew = 0; // Guard flag.
if ( fLogMemory && !fInOpNew ) {
fInOpNew = 1;
clog << "Memory block " << ++cBlocksAllocated
<< " allocated for " << stAllocateBlock
<< " bytes\n";
fInOpNew = 0;
}
return malloc( stAllocateBlock );
}
// User-defined operator delete.
void operator delete( void *pvMem ) {
static int fInOpDelete = 0; // Guard flag.
if ( fLogMemory && !fInOpDelete ) {
fInOpDelete = 1;
clog << "Memory block " << cBlocksAllocated--
<< " deallocated\n";
fInOpDelete = 0;
}
free( pvMem );
}
int main( int argc, char *argv[] ) {
fLogMemory = 1; // Turn logging on
if( argc > 1 )
for( int i = 0; i < atoi( argv[1] ); ++i ) {
char *pMem = new char[10];
delete[] pMem;
}
fLogMemory = 0; // Turn logging off.
return cBlocksAllocated;
}
上述程式碼可用來偵測「記憶體流失」,即是在可用存放區中配置但從未釋放的記憶體。 為了偵測外洩,全域 new 和 delete 運算子會重新定義,以計算記憶體的配置和解除配置。
編譯器支援類別宣告中的成員陣列 new 和 delete 運算子。 例如:
// spec1_the_operator_delete_function2.cpp
// compile with: /c
class X {
public:
void * operator new[] (size_t) {
return 0;
}
void operator delete[] (void*) {}
};
void f() {
X *pX = new X[5];
delete [] pX;
}