假设我有以下函数(其中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(...)
或其他方式。然后,它必须将指向该对象的指针返回给您。它不需要通过返回值返回,而是需要传入一个pointer-to-pointer-to-ID3D11VertexShader。它说"我制作了一个新对象。我有它的地址,即pointer-to-ID3D11VertexShader。但为了给你地址,我需要你告诉我把它写在哪里。给我pointer-to-ID 3D11Vertex Shader的地址,我会在那里写。即给我一个pointer-to-pointer-to-ID3D11VitexShader"。
这就解释了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
)。
(..待续..)
我将尝试直接回答造成混乱的可能原因。我对这句话感兴趣(我的重点):
。。。它是返回一个复制的指针,该指针指向与sharedptr容器中引用的内存地址相同的内存地址,还是完全是一个已复制并返回的全新指针
你在这里有点误用了"指针"这个词。这个片段并不清楚:">全新的指针已经被复制"。
指针只是一个地址。如果你复制了地址,你就有了另一份地址。基本的"顶点着色器"(VertexShader)保持不变。您有一个"顶点着色器"。你可以随心所欲地拥有地址(即指针)的副本,这不会改变任何事情。
所以,也许你想问"get()只是返回shared_ptr拥有的对象的地址吗?或者,它是否创建了着色器对象的副本(然后有不同的地址)然后返回复制对象的地址?答案是前者,它只是返回地址。我想你可能已经怀疑了。它返回地址,但您必须将其存储在某个地方,而这种存储必然涉及复制指针。
现在,再看一下新代码:
GVertexShader* raw_VertexShader = NULL;
mRenderData->Device->CreateVertexShader(
mCompiledShader->GetBufferPointer(),
mCompiledShader->GetBufferSize(),
NULL,
&raw_VertexShader
);
mVertexShader.reset(raw_VertexShader);
在这已经执行之后,仍然只有一个VertexShader
。它有一个地址。但该地址已被"写"在的两个位置。有两个东西知道着色器在哪里。raw_VertexShader
变量仍然有地址。并且该地址的另一个副本被构建到CCD_ 28中。
考虑一下这两个位置。您不能安排D3D将其新创建对象的地址直接写入mVertexShared
。您可以将其写入raw_VertexShader
,然后将其复制到中
记住,我们只是在这里复制一个地址。地址就像一张纸片,想想你住的大楼的地址。我们可以让D3D把它写在一张纸上,然后我们可以把它复制到"特殊"的shared_ptr纸片上。仅仅因为我们在复制地址,并不意味着这座建筑被复制了!
在调用CreateVertexShared之后,在执行rest()之前,您确实知道顶点的地址。您还知道您将该地址存储在何处。您已将其存储在raw_VertexShader
中。这意味着你知道raw_VertexShader
的地址-你知道一张写有着色器地址的纸的地址。最后一个地址,一张纸的地址,是传递到CreateVector的地址。你说"有一张纸,我想让你创建一个顶点着色器,然后在上面写下它的地址。你问哪张纸?我会给你这张纸的地址。"。我们确实知道在那张纸上写了什么(它有VertexShader的地址),但我们不知道那张纸到底在哪里。
删除
删除raw_VertexShader是否会影响mVertexShader.get()内的指针,或者,当指向的内存相同时,指针本身完全不同?
在C/C++中,您不会真正"删除"指针。您只需删除指针指向的对象。想象一下,一张写有房子地址的纸。想象一下,你有另一张地址相同的纸。现在,如果你打电话给delete pointer1
,你只会拆除其中一栋房子。房子将被摧毁,土地将提供给其他人,他们可能想在未来的某个时候在那里建一家商店。但这两张纸都不会改变。他们仍然会在那里有地址,而且他们会有相同的地址。但这只是一个无效的地址。shared_ptr将无效,您的程序最终将崩溃(甚至更糟!)。
如果需要,可以使用raw_VertexShader=NULL;
关闭该函数。这只会把地址从那张纸上抹去。这可能就是你想要的。你想对自己说"shared_ptr拥有对象,它应该是我访问着色器的唯一机制。因此,我会保持整洁,并擦除我拥有的地址的任何备用副本。">
然而,应该注意的是,raw_VertexShader=NULL;
什么都不做。这是正确的。我只是把它当作一种教育工具。它不会影响shared_ptr内的纸张。
(很抱歉回答太长,我没有时间回答太短。)