[英]Are pointers to memory copied during data transfer to/from function return values/arguments and class requests?
假設我具有以下功能(其中mVertexShader
是std::shared_ptr< ID3D11VertexShader >
類成員,而mRenderData
只是保存其他D3D內容的POD):
void VertexShader::CreateShader( void )
{
GVertexShader* raw_VertexShader = mVertexShader.get();
mRenderData->Device->CreateVertexShader(
mCompiledShader->GetBufferPointer(),
mCompiledShader->GetBufferSize(),
NULL,
&raw_VertexShader
);
delete raw_VertexShader;
raw_VertexShader = NULL;
}
因為mVertexShader.get()
返回原始指針,將刪除raw_VertexShader
效果內部的指針mVertexShader.get()
或者,當被指向的存儲器來是一樣的,指針本身是完全不同的?
我猜是后者,因此為什么首先要存在引用指針,但是我想確保我有正確的主意。
注意:我目前正在使用MSVC ++,盡管很高興得知MSVC ++和MinGW / GCC的實現方式是否存在重大差異。
注意2:如果我不清楚,我深表歉意–我在這里完全不是指D3D的語義,而是有關內存管理,數據傳輸和動態內存分配的C ++本身語言的語義。 從這個意義上講,我試圖確切地了解從函數返回原始指針時會發生什么。
即,假設mVertexShader
正在存儲指向任意VertexShader的簡單原始指針,當我調用其get()
方法時,它是否返回一個復制的指針,該指針指向與shared_ptr
容器內的引用相同的內存地址,或者它是一個完全新的指針,已經被復制並返回,並且內存指向另一個地址嗎?
實際上,這完全與std::shared_ptr
的語義有關。 該語言非常高興地支持以下方法:返回指向對象的指針,創建對象的副本並返回指向該對象的指針,或對對象上的引用計數進行碰撞並返回指向該對象的指針。 您可以根據需要執行所有這些操作。
共享指針上的get
方法僅將原始指針返回到對象。 沒有副本。 永遠不要 delete
此指針,原因有兩個:
另一個對象可能具有指向同一對象的共享指針,因此您將從該對象下面刪除該對象。 (這就是共享指針的要點。)
當最后一個共享指針消失時,該對象將被刪除。 兩次刪除對象是UB,可能會導致代碼崩潰。 (這也是共享指針的要點。)
讓共享指針完成其工作。
請注意,如果調用共享指針上的get
,則應確保將共享指針保留了很長時間,只要您可以使用獲得的原始指針即可。 否則,對象可能會不復存在。 共享指針的邏輯是,只要您在該對象周圍保持至少一個共享指針,該對象就不會被刪除,並且當最后一個此類共享指針消失時,該對象會自動刪除。
這個問題是關於Direct3D的。
(免責聲明:我一生中從未做過任何D3D,我只是在閱讀文檔的基礎上盡力而為,但我想我就在這里!)
您正在調用一個名為CreateVertexShader
的方法。 沒關系shared_ptr
東西。 您應該像這樣啟動您的方法:
GVertexShader* raw_VertexShader = NULL; // don't initialize
// with get() or anything like that.
mRenderData->Device->CreateVertexShader(
mCompiledShader->GetBufferPointer(),
mCompiledShader->GetBufferSize(),
NULL,
&raw_VertexShader
);
// The raw_VertexShader will *not* be NULL any more. It will have the address
// of the shader that was created by CreateVertexShader.
該方法(我假設)將創建一個VertexShader。 它將在內存中所需的任何地方創建它,可能是通過new VertexShaderImplementation(...)
或其他方式。 然后,它必須將指向該對象的指針返回給您。 而不是通過返回值返回它,它要求您傳遞一個指向ID3D11VertexShader的指針。 這是在說: “我已經創建了一個新對象。我有它的地址,即指向ID3D11VertexShader的指針。但是,為了讓我給您地址,我需要您告訴我在哪里寫下它。指向ID3D11VertexShader的指針的地址,並將其寫在其中。即,給我一個指向ID3D11VertexShader的指針。
這說明了CreateVertexShader
使用**ID3D11VertexShader
作為其參數的事實。 注意兩個*
s。
那么,shared_ptr與這有什么關系?
(簡單的答案:沒有!您可能會忘記了shared_ptr,但這不是現代的C ++。)
當CreateVertexShader返回時,該指針不屬於任何共享指針。 您必須先獲取指針(即運行CreateVertexShader
),然后才能將該對象的所有權提供給shared_ptr。
您要嘗試做的是安排D3D直接將指針直接“塞入”您的shared_ptr
。 但這行不通。 您需要先讓CreateVertexShader
“返回”其答案, 然后才能將其放入shared_ptr
mVertexShader.reset(raw_VertexShader); // To be called *after* CreateVertexShader
mVertexShader
現在擁有該指針,並且它是唯一擁有此指針的shared_ptr
。 如果那里已經有其他着色器對象,則可以立即將其刪除(取決於它是否是它的最后一個shared_ptr
)。
(.. 未完待續 ..)
我將嘗試直接回答可能引起混亂的原因。 我對這句話很感興趣(我的重點是):
...它是否返回一個復制的指針 ,該指針指向與shared_ptr容器內的引用相同的內存地址,或者它是一個完全被復制並返回的全新指針 ?
您在這里誤用了“指針”一詞。 這個片段不清楚:“已經被復制的 全新指針 ”。
指針只是一個地址。 如果您復制地址,則只有該地址的另一個副本。 基礎的VertexShader保持不變。 您有一個VertexShader。 您可以根據需要擁有任意數量的地址(即指針)副本,它不會改變任何內容。
因此,也許您想問:“ get()是否只返回shared_ptr擁有的對象的地址? 或者 ,它會創建Shader對象的副本(然后具有不同的地址),然后返回該對象的地址。復制對象嗎?答案是前者,它只是返回地址,我想您可能已經懷疑了,它返回了地址,但是您必須將其存儲在某個地方,並且這種存儲必然涉及到復制指針。
現在,再次查看新代碼:
GVertexShader* raw_VertexShader = NULL;
mRenderData->Device->CreateVertexShader(
mCompiledShader->GetBufferPointer(),
mCompiledShader->GetBufferSize(),
NULL,
&raw_VertexShader
);
mVertexShader.reset(raw_VertexShader);
執行此操作后,仍然只有一個VertexShader
。 它有一個地址。 但是該地址已在兩個地方“寫下”。 有兩件事知道着色器在哪里。 raw_VertexShader
變量仍然具有該地址。 該地址的另一個副本內置在mVertexShader
。
考慮這兩個位置。 您無法安排D3D將其新創建的對象的地址直接寫入mVertexShared
。 您可以將其寫入到raw_VertexShader
,然后將其復制到其中。
請記住,我們只是在此處復制一個地址。 地址就像廢紙一樣,請考慮您所居住建築物的地址。我們可以讓D3D將其寫在一張紙上,然后將其復制到“特殊” shared_ptr紙上。 僅僅因為我們正在復制地址,並不意味着該建築物正在重復!
在調用CreateVertexShared之后,在rest()之前,您確實知道頂點的地址。 您還知道該地址的存儲位置。 您已將其存儲在raw_VertexShader
。 這意味着您知道raw_VertexShader
的地址-您知道在上面寫有着色器地址的紙的地址。 最后一個地址(即一塊地址)是傳遞給CreateVector的地址。 您說: “有一張紙,我要您創建一個Vertex Shader,然后在其上寫地址。您問哪張紙?我會給您這張紙的地址。” 我們找不到shared_ptr內的紙的地址。 我們確實知道在那張紙上寫了什么(它有VertexShader的地址),但我們不知道那張紙到底在哪里。
刪除
將刪除raw_VertexShader會影響mVertexShader.get()中的指針,還是在指向的內存相同的同時,指針本身完全不同?
在C / C ++中,您實際上並沒有“刪除”指針。 您只需刪除指針指向的對象。 想象一下一張紙上的房子的地址。 假設您還有另一張地址相同的紙。 現在,如果您調用delete pointer1
,則將拆除其中一間房屋。 這所房屋將被摧毀,將來可能會在其他地方為其他人提供土地的土地也可以使用。 但是這兩張紙都不會改變。 他們仍將有地址,並且他們將擁有相同的地址。 但這將是一個無效的地址。 shared_ptr將無效,並且您的程序最終將崩潰(甚至更糟!)。
如果需要,可以使用raw_VertexShader=NULL;
關閉該函數raw_VertexShader=NULL;
。 這將簡單地擦除那張紙上的地址。 這可能就是你想要的。 您想對自己說:“ shared_ptr擁有該對象,它應該是我用來訪問着色器的唯一機制。因此,我將保持整潔,並將刪除我擁有的地址的所有備用副本。”
但是應該注意raw_VertexShader=NULL;
什么也沒做 。 這是對的。 我只是將其用作教育工具。 它不影響內部的shared_ptr的一張紙。
(很抱歉,答案很長,我沒有時間簡短。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.