newdelete 運算子

C++ 支援使用 newdelete 運算子來動態配置和解除配置物件。 這些運算子會配置記憶體給集區中稱為 免費存放區 的物件(也稱為 堆積 )。 運算子 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 擲回例外狀況。 或者,如果您已使用放置表單 new(std::nothrow) ,或已在非擲回 operator new 支援中連結,則會傳回 nullptr 如需詳細資訊,請參閱 配置失敗行為

下表說明函式的兩個 operator new 範圍。

函式的範圍 operator new

運算子 範圍
::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 提供的引數會當做 chInit 引數傳遞至 Blanks::operator new 。 不過,全域 operator new 函式會隱藏,導致下列程式碼產生錯誤:

Blanks *SomeBlanks = new Blanks;

編譯器支援類別宣告中的成員陣列 newdelete 運算子。 例如:

class MyClass
{
public:
   void * operator new[] (size_t)
   {
      return 0;
   }
   void   operator delete[] (void*)
   {
   }
};

int main()
{
   MyClass *pMyClass = new MyClass[5];
   delete [] pMyClass;
}

配置失敗行為

newC++ 標準程式庫中的 函式支援 C++ 標準中指定的行為,因為 C++98。 當配置要求記憶體不足時, operator newstd::bad_alloc 擲回例外狀況。

舊版 C++ 程式碼傳回失敗配置的 Null 指標。 如果您有預期非擲回版本的 new 程式碼,請將程式連結至 nothrownew.obj 。 檔案會 nothrownew.obj 以配置失敗時傳 nullptr 回的版本取代全域 operator newoperator new 不再擲回 std::bad_alloc 。 如需和其他連結器選項檔案的詳細資訊 nothrownew.obj ,請參閱 連結選項

您無法將檢查全域 operator new 例外狀況的程式碼與檢查相同應用程式中 Null 指標的程式碼混合在一起。 不過,您仍然可以建立以不同方式運作的類別本機 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 * 單一引數,其中包含要解除配置之物件的指標。 第二種形式大小解除配置會採用兩個引數:第一種是要解除配置的記憶體區塊指標,第二種是要解除配置的位元組數目。 這兩種表單的傳回類型為 voidoperator delete 無法傳回值)。

第二個表單的意圖是加快搜尋要刪除之物件的正確大小類別。 此資訊通常不會儲存在配置本身附近,而且可能未快取。 當基類的函式用來刪除衍生類別的物件時 operator delete ,第二個表單很有用。

函式是靜態的 operator delete ,因此不能是虛擬的。 函 operator delete 式會遵守存取控制,如 Member-存取控制 中所述

下列範例顯示設計來記錄記憶體配置和解除配置的使用者定義 operator newoperator 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;
}

上述程式碼可用來偵測「記憶體外泄」,也就是配置於免費存放區但從未釋放的記憶體。 為了偵測流失,全域 newdelete 運算子會重新定義以計算記憶體的配置和解除配置。

編譯器支援類別宣告中的成員陣列 newdelete 運算子。 例如:

// 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;
}