[英]How C++ placement new works?
這個問題是為了確認我對概念的理解是正確的,並就使用風格和可能的優化采取專家意見。
我正在嘗試理解“placement new”,以下是我想出的程序……
#include <iostream>
#include <new>
class A {
int *_a;
public:
A(int v) {std::cout<<"A c'tor clalled\n";_a= new int(v);}
~A() {std::cout<<"A d'tor clalled\n"; delete(_a);}
void testFunction() {std::cout<<"I am a test function &_a = "<<_a<<" a = "<<*_a<<"\n";}
};
int main()
{
A *obj1 = new A(21);
std::cout<<"Object allocated at "<<obj1<<std::endl;
obj1->~A();
std::cout<<"Object allocated at "<<obj1<<std::endl;
obj1->testFunction();
A *obj2 = new(obj1) A(22);
obj1->testFunction();
obj2->testFunction();
delete(obj1);// Is it really needed now? Here it will delete both objects.. so this is not the right place.
//obj1->testFunction();
//obj2->testFunction();
return 0;
}
當我運行這個程序時,我得到以下 o/p
A c'tor clalled
Object allocated at 0x7f83eb404c30
A d'tor clalled
Object allocated at 0x7f83eb404c30
I am a test function &_a = 0x7f83eb404c40 a = 21
A c'tor clalled
I am a test function &_a = 0x7f83eb404c40 a = 22
I am a test function &_a = 0x7f83eb404c40 a = 22
A d'tor clalled
I am a test function &_a = 0x7f83eb404c40 a = 0
I am a test function &_a = 0x7f83eb404c40 a = 0
我有以下問題...
還請指出您看到我可以改進的任何內容或只是不要嘗試。 也歡迎任何好的參考或閱讀。
這真的非常簡單: new
可以被認為是做兩件事:
不能保證malloc
被實現實際使用,但通常是這樣。 你不能假設它的實現,但為了理解它是一個好的假設。
因此,以下內容被認為是等效的:
auto obj1 = new std::string("1");
// ↑ can be thought of as equivalent to ↓
auto obj2 = (std::string*)malloc(sizeof(std::string));
new(obj2) std::string("2");
delete
:
delete obj1;
// ↑ can be thought of as equivalent to ↓
obj2->~string();
free(obj2);
然后,當您看到new
和delete
的真實情況時,您可以輕松地對這一切進行推理:分配后跟構造函數調用,以及析構函數調用后跟釋放。
當您使用placement new
,您已決定單獨處理第一步。 內存仍然必須以某種方式分配,您只需完全控制它的發生方式以及內存來自何處。
因此,您必須分別跟蹤兩件事:
記憶的壽命。
對象的生命周期。
下面的代碼演示了這些是如何相互獨立的:
#include <cstdlib>
#include <string>
#include <new>
using std::string;
int main() {
auto obj = (string*)malloc(sizeof(string)); // memory is allocated
new(obj) string("1"); // string("1") is constructed
obj->~string (); // string("1") is destructed
new(obj) string("2"); // string("2") is constructed
obj->~string (); // string("2") is destructed
free(obj); // memory is deallocated
}
如果對象的生命周期超過內存的生命周期,則您的程序具有 UB。 確保內存始終比對象的生命周期長。 例如,這有 UB:
void ub() {
alignas(string) char buf[sizeof(string)]; // memory is allocated
new(buf) string("1"); // string("1") is constructed
} // memory is deallocated but string("1") outlives the memory!
但這沒關系:
void ub() {
alignas(string) char buf[sizeof(string)]; // memory is allocated
new(buf) string("1"); // string("1") is constructed
buf->~string(); // string("1") is destructed
} // memory is deallocated
請注意您需要如何使用alignas
正確對齊自動緩沖區。 缺少任意類型的alignas
導致 UB。 它可能看起來有效,但這只會誤導您。
在某些特定類型中,不調用析構函數和不正確對齊內存不會導致 UB,但您永遠不應該對類型假設這樣的事情。 調用你的析構函數並進行對齊,如果它被證明是不必要的,它不會花費你任何東西 - 不會為這種類型生成額外的代碼。
struct S {
char str[10];
}
這可能是CodeReview.SE 的內容,讓我在回答您的問題之前對您的源代碼進行評論。
A *obj1 = new A(21);
std::cout<<"Object allocated at "<<obj1<<std::endl;
obj1->~A();
您通常永遠不會在不是用placement-new 創建的對象上調用析構函數。 在你的情況下,你破壞了舊的並用placement-new 構建了一個新的。 即使這可行,您也應該實現一些重置功能來重置您的對象,而不是破壞和構建一個新對象。
17 obj1->testFunction();
這是 UB。 您已經破壞了該對象,您不應該對其調用任何方法。
18 A *obj2 = new(obj1) A(22);
19 obj1->testFunction();
20 obj2->testFunction();
這很好,但請注意obj1
和obj2
是完全相同的對象。
21 delete(obj1);// Is it really needed now? Here it will delete both objects.. so this is not the right place.
你的評論是錯誤的。 您不是刪除兩個對象,而是刪除一個,稍后再刪除。
22 obj1->testFunction();
23 obj2->testFunction();
這又是 UB,不要在解構或刪除的對象上調用方法。 對於您的問題:
成員 _a 是動態分配的(沒有新的位置)。 那么為什么它為 obj1 和 obj2 獲得相同的地址。 這只是巧合嗎?
不要稱它們為obj1
和obj2
因為這兩個變量指向同一個對象,但是是的,這是巧合。 在第一個對象被破壞並釋放此內存后,第二個對象分配了剛剛釋放的相同數量的內存,分配器決定為您提供完全相同的內存。
在第 15 行撥打 D'tor 是一個好習慣嗎?
不,這不對。 為什么需要調用析構函數的示例很少,其中之一是您的對象是由placement-new 創建的。 在您的示例中,這沒有副作用,因為您在解構舊對象后在同一位置構造了一個新對象,並且新對象與舊對象具有相同的類型,否則這可能會以某種方式嚴重破壞。
現在更多關於您刪除后的評論。 讓我們看看new
和placement-new 實際上做了什么。
一個新的:
this
)被設置為分配器獲得的內存塊。刪除則相反:
現在到placement-new:placement-new 只是跳過第一步(分配內存)並調用新對象的構造函數,並將this
設置為您傳遞的地址。 因此,placement-new 的反面只是調用析構函數,因為不存在placement-delete。
這意味着對於您的代碼,在您調用析構函數之后,您的第一個對象死亡,但您從未將內存歸還,這就是您可以在該內存中構造一個新對象的原因。 現在當你調用 delete 時,第一個對象不再存在,只有它使用的內存,但是相同的內存現在被第二個對象阻塞,因此當你調用 delete 時,你不會刪除兩個對象,你只刪除第二個一個(你解構它,然后釋放內存塊)。
您可以在isocpp 的 faq 中閱讀有關主題放置新以及何時調用析構函數的更多信息
C++ 放置新功能如何工作?
...
我正在嘗試理解“placement new”,以下是我想出的程序……
這兩個答案都很棒。 但是您也想知道它是如何工作的,因此,我將添加程序集的解釋:
A *obj1 = new A(21);
:call operator new(unsigned long)
mov esi, 21
mov rdi, rax
mov rbx, rax
call A::A(int)
A *obj2 = new(obj1) A(22);
mov esi, 22
mov rdi, rbx
call A::A(int)
這就是它的工作原理,足夠清楚,不需要更多解釋,對吧?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.