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 例外狀況。 或者,如果您已使用 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;

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

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 的版本取代全域 nullptroperator 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;
   }
}

當您使用 nothrownew 形式時,您可測試配置失敗,如下列範例所示:

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