[英]Placement New in C++
在閱讀C ++ Primer Plus時,我幾乎沒有關於新位置的問題。
書中的示例代碼如下:
class JustTesting{
private:
string words;
int number;
public:
JustTesting(const string & s = "Just Testing", int n = 0){
number = n;
words = s;
//some code here
}
~JustingTesting(){}
};
char * buffer = new char[BUF]; //get a block of memory
JustTesting *pc1, *pc2;
pc1 = new (buffer) JustTesting; //Place object in buffer
pc2 = new JustTesting("Heap1",20); //Place object on heap
//some code
JustTesting *pc3, *pc4;
pc3 = new (buffer) JustTesting("Bad Idea", 6);
pc4 = new JustTesting("Heap2", 10);
//some code
delete pc2; //free Heap1
delete pc4; //free Heap2
pc3->~JustTesting(): //Does the order of these two destructor call
pc1->~JustTesting(); // matters?
delete[] buffer; //free buffer
作者說,我們不能使用
delete pc1;
要么
delete pc3;
刪除他們指向的對象,因為刪除與new一起使用但不與placement new一起使用。 例如,指針pc3不接收new返回的地址, delete pc3
將導致運行時錯誤。
問題是:首先,pc3指向的對象是否會覆蓋pc1指向的對象? 如果不是,兩個不同的對象如何保持在同一個地址中。 如果是,為什么我們仍然可以顯式調用析構函數( pc1->~JustTesting();
)來釋放對象的內存。
問題二:這兩個顯式析構函數的順序是否有問題?
問題三:“指針pc3沒有收到新的返回地址”是什么意思? 新的不同地址是否由賦值給出的地址返回?
謝謝!
一切都是對的,直到......
pc3 = new (buffer) JustTesting("Bad Idea", 6);
這會調用未定義的行為(不?)。 你已經在buffer
構造了一個JustTesting
類型的對象,但是你沒有破壞它! 與此同時,要創建在同一位置還有另一個目的。 然后,第一個對象被破壞(雖然,在標准的思想中,它仍然存在於一個平行的宇宙中)。
您不能對指向未通過operator new
分配(和構造)的任何內容的指針執行delete
operator new
。 類似地,您只能使用operator delete[]
來銷毀和取消分配由operator new[]
創建的數組。
現在,“placement new”只是一個直接調用構造函數的奇特名稱。 因此, new(buff) Type(...)
只是對Type
s構造函數的調用,並將this
設置為buff
。 並且,與上述內容相似,您只能破壞已構建的內容。
如果您使用自動存儲, operator new
或任何其他隱式RAII一致的方法,負責自動分配,構造和破壞您的對象(或者當您指定它時),然后在這樣的方法中調用對象的析構函數上下文將導致析構函數被調用兩次, 即未定義的行為。
現在,碰巧你 (我會再重復一次嗎? 你! )是決定何時以及如何獲取對象內存的人,然后環境沒有改變猜測何時銷毀或取消分配對象。 因此,一旦你明確地調用了對象的析構函數,曾經包含它的內存由你負責釋放,不知何故,如果有的話。
可以這樣想。 形式為ptr = new X(...)
的表達式可以完美地實現為......
ptr = malloc(sizeof(X));
new(ptr) X(...);
operator delete
變為......
ptr->~X();
free(ptr);
問題是:首先,pc3指向的對象是否會覆蓋pc1指向的對象?“
這很危險,兩個對象都會嘗試共享同一塊內存。
如果是,為什么我們仍然可以顯式調用析構函數(pc1-> ~JustTesting();)來釋放對象的內存。
這不會釋放通過placement new創建的對象的內存。 它只是調用對象的析構函數。 傳統的刪除操作符將調用析構函數,然后通過假設正常分配來嘗試釋放內存。 在這個例子中,由於已經引發的共享問題,它將有效地嘗試在該內存塊上調用兩次析構函數。
問題二:這兩個顯式析構函數的順序是否有問題?
不,在任何一種情況下,第二次通話都可能有問題。
問題三:“指針pc3沒有收到新的返回地址”是什么意思? 地址是否按照賦值給出的地址返回新的不同?
new返回的地址是堆中新分配的內存塊(假設默認的新運算符未被覆蓋)。 placement new返回的地址只是你給它的內存塊的地址。
首先,pc3指向的對象是否會覆蓋pc1指向的對象?
是。 它們都將存儲在buffer
指向的地址。 這可能是未定義的行為 ,無論如何通常都是一個壞主意(除非你確定該類的析構函數沒有做任何重要的事情)。
如果是,為什么我們仍然可以顯式調用析構函數(pc1-> ~JustTesting();)來釋放對象的內存。
最有可能的是, pc1
和pc3
指向同一位置。 我不確定這是否有保證,但我無法想象為什么在任何實現中都不會出現這種情況。
如果pc1
和pc3
指向相同的位置,那么這個問題就像問為什么以下代碼有效:
char *p1 = new char[50];
char *p2 = p1;
delete [] p2;
“指針pc3沒有收到新的返回地址”是什么意思? 地址是否按照賦值給出的地址返回新的不同?
可能是措辭不好。 很明顯, pc3
確實收到了new
返回的地址 - 您可以在代碼中看到它:
pc3 = new // ... other stuff ...
我懷疑他們的意思是地址不是由new
分配的。 通常new
分配一塊內存來保存一個對象,然后在該內存塊中構造一個對象。 使用placement new
,它只在提供的內存塊中構造一個對象。
同樣, delete
將銷毀其指針傳遞給它的對象,然后釋放先前在該地址分配的內存塊。 delete
未分配new
的對象是未定義的行為 - 想想JustTesting a("Object on stack", 30); delete &a;
JustTesting a("Object on stack", 30); delete &a;
。
如果你很幸運 , delete pc3
可能與pc3->~JustTesting(); delete [] buffer;
具有相同的效果pc3->~JustTesting(); delete [] buffer;
pc3->~JustTesting(); delete [] buffer;
,所以它可能不會崩潰 - 雖然然后delete [] buffer;
因為你要刪除一個對象兩次會崩潰。 你絕對不應該依賴它。 唯一安全的做法是不delete
使用placement new
分配的對象。
問題是:首先,pc3指向的對象是否會覆蓋pc1指向的對象?
是。
如果是,為什么我們仍然可以顯式調用析構函數(pc1-> ~JustTesting();)來釋放對象的內存。
pc
尚不存在,任何調用它的函數都是UB。
問題二:這兩個顯式析構函數的順序是否有問題?
你根本不應該一起打電話給他們。
問題三:“指針pc3沒有收到新的返回地址”是什么意思? 新的不同地址是否由賦值給出的地址返回?
我認為這應該意味着“指針pc3沒有收到新分配的地址”。 placement new只返回您傳遞給它的地址。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.