嘗試配置和初始化指定或預留位置類型的物件或物件的陣列,並傳回物件適當類型的非零指標 (或陣列的初始物件)。
語法
new-expression
:
::
opt new
new-placement
opt new-type-id
new-initializer
opt
::
opt new
new-placement
opt (
type-id
)
new-initializer
opt
new-placement
:
(
expression-list
)
new-type-id
:
type-specifier-seq
new-declarator
opt
new-declarator
:
ptr-operator
new-declarator
opt
noptr-new-declarator
noptr-new-declarator
:
[
expression
]
attribute-specifier-seq
opt
noptr-new-declarator
[
constant-expression
]
attribute-specifier-seq
opt
new-initializer
:
(
expression-list
opt )
braced-init-list
備註
如果失敗,則 new
會傳回零或擲回例外狀況。 如需詳細資訊,請參閱 new
和 delete
運算子。 您可以變更這個預設行為,作法是撰寫自訂例外狀況處理常式,並將您的函式名稱當做引數來呼叫 _set_new_handler
執行階段程式庫函式。
如需如何在 C++/CLI 和 C++/CX 中於受控堆積上建立物件的相關資訊,請參閱 gcnew。
注意
Microsoft C++ 元件延伸模組 (C++/CX) 會提供對 new
關鍵字的支援以加入 vtable 位置項目。 如需詳細資訊,請參閱 new
(vtable 中的新位置)
當使用 new
來配置 C++ 類別物件的記憶體時,會在配置記憶體之後呼叫物件的建構函式。
使用 delete
運算子來解除配置 new
運算子配置的記憶體。 使用 delete[]
運算子來刪除 new
運算子配置的陣列。
下列範例會先配置然後再釋放大小為 dim
乘以 10 個字元的二維陣列。 配置多維陣列時,第一個維度以外的所有維度都必須是評估為正值的常數運算式。 最左邊的陣列維度可以是任何評估為正值的運算式。 使用 new
運算子配置陣列時,第一個維度可以是零;new
運算子會傳回唯一指標。
char (*pchar)[10] = new char[dim][10];
delete [] pchar;
type-id
不能包含 const
、volatile
、類別宣告或列舉宣告。 下列運算式的格式不正確:
volatile char *vch = new volatile char[20];
new
運算子不會配置參考類型,因為它們不是物件。
new
運算子不能用來配置函式,但是可以用來配置函式的指標。 下列範例會配置然後再釋放含七個傳回整數之函式指標的陣列。
int (**p) () = new (int (*[7]) ());
delete p;
如果您使用運算子 new
而不使用任何額外的引數,並使用 /GX
、/EHa
或 /EHs
選項進行編譯,則編譯器會在建構函式擲回例外狀況時產生呼叫運算子 delete
的程式碼。
下列清單說明 new
的文法元素:
new-placement
如果您多載 new
,提供傳遞額外引數的方式。
type-id
指定要配置的類型;它可以是內建或使用者定義的類型。 如果類型規格是複雜的,請以括號括住類型規格以強制繫結的順序。 此類型可能是其類型由編譯器決定的預留位置 (auto
)。
new-initializer
提供初始化物件的值。 不能為陣列指定初始設定式。 new
運算子只有在類別具有預設建構函式時,才會建立物件的陣列。
noptr-new-declarator
指定陣列的界限。 配置多維陣列時,第一個維度以外的所有維度都必須是評估為可轉換為 std::size_t
的正值的常數運算式。 最左邊的陣列維度可以是任何評估為正值的運算式。 attribute-specifier-seq
會套用至相關聯的陣列類型。
範例:配置和釋放字元陣列
下列程式碼範例會配置一個字元陣列和一個 CName
類別物件,然後加以釋放。
// expre_new_Operator.cpp
// compile with: /EHsc
#include <string.h>
class CName {
public:
enum {
sizeOfBuffer = 256
};
char m_szFirst[sizeOfBuffer];
char m_szLast[sizeOfBuffer];
public:
void SetName(char* pszFirst, char* pszLast) {
strcpy_s(m_szFirst, sizeOfBuffer, pszFirst);
strcpy_s(m_szLast, sizeOfBuffer, pszLast);
}
};
int main() {
// Allocate memory for the array
char* pCharArray = new char[CName::sizeOfBuffer];
strcpy_s(pCharArray, CName::sizeOfBuffer, "Array of characters");
// Deallocate memory for the array
delete [] pCharArray;
pCharArray = NULL;
// Allocate memory for the object
CName* pName = new CName;
pName->SetName("Firstname", "Lastname");
// Deallocate memory for the object
delete pName;
pName = NULL;
}
例如:new
運算子
如果您使用 new
運算子的放置格式 (該格式具有比大小更多的引數),如果建構函式擲回例外狀況,編譯器就不支援 delete
運算子的放置格式。 例如:
// expre_new_Operator2.cpp
// C2660 expected
class A {
public:
A(int) { throw "Fail!"; }
};
void F(void) {
try {
// heap memory pointed to by pa1 will be deallocated
// by calling ::operator delete(void*).
A* pa1 = new A(10);
} catch (...) {
}
try {
// This will call ::operator new(size_t, char*, int).
// When A::A(int) does a throw, we should call
// ::operator delete(void*, char*, int) to deallocate
// the memory pointed to by pa2. Since
// ::operator delete(void*, char*, int) has not been implemented,
// memory will be leaked when the deallocation can't occur.
A* pa2 = new(__FILE__, __LINE__) A(20);
} catch (...) {
}
}
int main() {
A a;
}
初始化使用 new
配置的物件
選用的 new-initializer
欄位包含在 new
運算子的文法中。 此欄位可讓您使用使用者定義的建構函式初始化新物件。 如需如何執行初始化的詳細資訊,請參閱初始設定式。 下列範例將示範如何使用具有 new
運算子的初始化運算式:
// expre_Initializing_Objects_Allocated_with_new.cpp
class Acct
{
public:
// Define default constructor and a constructor that accepts
// an initial balance.
Acct() { balance = 0.0; }
Acct( double init_balance ) { balance = init_balance; }
private:
double balance;
};
int main()
{
Acct *CheckingAcct = new Acct;
Acct *SavingsAcct = new Acct ( 34.98 );
double *HowMuch = new double { 43.0 };
// ...
}
在此範例中,物件 CheckingAcct
是使用 new
運算子配置,但是未指定預設初始化。 因此會呼叫類別 Acct()
的預設建構函式。 然後以相同方式配置物件 SavingsAcct
,不過它會明確初始化為 34.98。 由於 34.98 屬於 double
類型,因此會呼叫接受該類型引數的建構函式處理初始化。 最後,非類別類型 HowMuch
會初始化為 43.0。
如果物件為類別類型且該類別具有建構函式 (如上述範例),則只有在符合下列其中一項條件時,物件才可以由 new
運算子初始化:
初始設定式運算式中提供的引數符合建構函式的引數。
類別有預設建構函式 (可以在沒有引數的情況下呼叫的建構函式)。
使用 new
運算子配置陣列時,不可明確執行每個元素的初始化,而只會呼叫預設建構函式 (如果有的話)。 如需詳細資訊,請參閱預設引數。
如果記憶體配置失敗 (operator new
傳回值 0),則不會執行初始化。 此行為可防止嘗試初始化不存在的資料。
就像使用函式呼叫,不會定義初始化運算式的評估順序。 此外,在記憶體配置發生之前,您不應該完全依賴於評估這些運算式。 如果記憶體配置失敗,而且 new
運算子傳回零,則初始設定式中的某些運算式可能不會完全評估。
使用 new
配置的物件的存留期
使用 new
運算子配置的物件不會在其所在的定義範圍結束時終結。 由於 new
運算子會傳回其所配置物件的指標,因此程式必須為指標定義適當的範圍,才能存取和刪除這些物件。 例如:
// expre_Lifetime_of_Objects_Allocated_with_new.cpp
// C2541 expected
int main()
{
// Use new operator to allocate an array of 20 characters.
char *AnArray = new char[20];
for( int i = 0; i < 20; ++i )
{
// On the first iteration of the loop, allocate
// another array of 20 characters.
if( i == 0 )
{
char *AnotherArray = new char[20];
}
}
delete [] AnotherArray; // Error: pointer out of scope.
delete [] AnArray; // OK: pointer still in scope.
}
一旦指標 AnotherArray
超出範例中的範圍,就無法刪除物件。
new
的運作方式
new-expression
(包含 new
運算子的運算式) 會執行三個動作:
為要配置的物件找出並保留存放區。 此階段完成時會配置正確數量的儲存體,但還不是物件。
將物件初始化。 初始化完成後,就會出現使配置存放區成為物件的足夠資訊。
傳回衍生自
new-type-id
或type-id
的指標類型物件的指標。 程式會使用此指標來存取新配置的物件。
new
運算子會叫用函式 operator new
。 對於任何類型的陣列,以及不是 class
、struct
或 union
類型的物件,會呼叫全域函式 ::operator new
來配置儲存體。 類別類型的物件可以根據類別定義各自的 operator new
靜態成員函式。
當編譯器遇到用來配置類型 T
物件的 new
運算子時,它會發出對 T::operator new( sizeof(T) )
的呼叫,如果沒有定義使用者定義的 operator new
,則為 ::operator new( sizeof(T) )
。 new
運算子可以配置物件所需正確數量的記憶體。
注意
operator new
的引數為類型 std::size_t
。 此類型是在 <direct.h>、<malloc.h>、<memory.h>、<search.h>、<stddef.h>、<stdio.h>、<stdlib.h>、<string.h> 和 <time.h> 中定義。
文法中的選項允許指定 new-placement
(請參閱 new
運算子的文法)。 new-placement
參數只能用於使用者定義的 operator new
實作;它可用於將額外的資訊傳遞至 operator new
。 如果類別 T 具有成員 operator new
,則具有 T *TObject = new ( 0x0040 ) T;
之類的 new-placement
欄位的運算式會轉譯為 T *TObject = T::operator new( sizeof( T ), 0x0040 );
,否則為 T *TObject = ::operator new( sizeof( T ), 0x0040 );
。
new-placement
欄位的原始用途是允許在使用者指定的位址配置取決於硬體的物件。
注意
雖然上述範例只示範 new-placement
欄位中的一個引數,但利用這種方式可額外傳遞到 operator new
的引數數量不受限制。
即使已針對類別類型 T
定義 operator new
,也可以明確地使用全域運算子 new
,如下列範例所示:
T *TObject = ::new TObject;
範圍解析運算子 (::
) 會強制使用全域 new
運算子。