2

假设我有以下功能(类成员在哪里,mVertexShader只是持有其他 D3D 东西的 POD):std::shared_ptr< ID3D11VertexShader >mRenderData

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,或者它是一个全新的完全被复制并返回的指针,内存被指向不同的地址?

4

2 回答 2

6

这实际上完全是关于std::shared_ptr. 该语言非常乐意支持返回指向对象的指针、复制对象并返回指向该对象的指针或增加对象的引用计数并返回指向它的指针的方法。如果你愿意,你可以做所有这些事情。

共享指针上的get方法只返回指向对象的原始指针。没有复制。您永远不 delete应该使用此指针,原因有两个:

  1. 另一个对象可能有一个指向同一个对象的共享指针,您将从它下面删除该对象。(这就是共享指针的意义所在。)

  2. 当最后一个共享指针消失时,该对象将被删除。删除一个对象两次是 UB,可能会导致您的代码崩溃。(这也是共享指针的意义所在。)

让共享指针完成它们的工作。

请注意,如果您调用get共享指针,则应确保保留该共享指针,只要您可能使用所获得的原始指针。否则,该对象可能不复存在。共享指针的逻辑是,只要在引用对象的周围至少保留一个共享指针,它就不会被删除,当最后一个这样的共享指针消失时,对象会自动删除。

于 2012-07-29T07:40:30.917 回答
1

这个问题是关于 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这解释了以 a**ID3D11VertexShader作为参数的事实。注意两个*s。

那么,shared_ptr 和这个有什么关系呢?

(简单的回答:什么都没有!你可以忘记 shared_ptr,但这不是现代 C++。)

当 CreateVertexShader 返回时,指针不被任何共享指针“拥有”。您必须先获取指针(即 run 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 的地址。你说“有一张纸,我要你创建一个顶点着色器,然后在上面写上它的地址。你问是哪张纸?我给你这张纸的地址。” 我们找不到 shared_ptr 中的那张纸的地址。我们确实知道写的是什么在那张纸上(它有 VertexShader 的地址),但我们不知道那张纸的确切位置。


删除

删除 raw_VertexShader 是否会影响 mVertexShader.get() 中的指针,或者,虽然指向的内存相同,但指针本身却完全不同?

在 C/C++ 中,您并没有真正“删除”指针。您只需删除指针指向的对象。想象一张纸,上面写着房子的地址。想象一下,您有另一张具有相同地址的纸。现在,如果你打电话delete pointer1,你只会拆除其中一所房子。房子将被摧毁,土地将提供给其他可能想在未来某个时候在那里开店的人。但这两张纸都不会改变。他们仍然会在那里拥有地址,并且他们将拥有相同的地址。但这只是一个无效的地址。shared_ptr 将无效,您的程序最终将崩溃(或更糟!)。

如果需要,您可以使用 关闭该功能raw_VertexShader=NULL;。这只会从那张纸上删除地址。这可能就是你想要的。您想对自己说“shared_ptr 拥有该对象,它应该是我用来访问着色器的唯一机制。因此,我会整理并删除我拥有的地址的任何备用副本。”

但是应该注意的是,raw_VertexShader=NULL;什么也没做。这是对的。我只是将它用作教育工具。它不会影响shared_ptr 内的那张纸。

(对不起,回答太长了,我没有时间做一个简短的回答。)

于 2013-10-18T16:43:13.597 回答