C ++ Directx精灵类,向量的问题

时间:2014-01-09 02:36:41

标签: c++ directx sprite vector

所以我用LPD3DXSPRITE创建了一个精灵类来保存精灵。它很棒,直到我在矢量中使用它。当向量创建副本并销毁其他精灵时,就会出现问题。当它这样做时,它会调用析构函数,它要求释放LPD3DXSPRITE对象。当这个对象被销毁时,副本不能再调用它,我得到一个内存错误。

我如何解决这个问题?我正在考虑一个解决方案,涉及让副本指向原件,然后指针将变为null,如果没有任何指向,表明析构函数应该杀死精灵,但这似乎有点疯狂。

我在下面提供了一些代码。另请注意,我还在学习C ++(我是专业的C#程序员)所以如果你看到任何疯狂的事情,请告诉我,不要对我有所了解。

Sprite.h

#ifndef SPRITE_H
#define SPRITE_H

#include <d3dx9.h>

// 
class Sprite
    {
    private:
        LPD3DXSPRITE sprite;

Sprite.cpp

#include "sprite.h"

// Copy constructor
Sprite::Sprite(Sprite &_copy)
{
    center = _copy.center;
    color = _copy.color;
    matrix = _copy.matrix;
    position = _copy.position;
    rotation = _copy.rotation;
    scale = _copy.scale;
    sourceRect = _copy.sourceRect;
    sprite = _copy.sprite;
    texture = _copy.texture;
}

// Full constructor which fully initializes the sprite.
Sprite::Sprite(LPDIRECT3DDEVICE9 _device, LPDIRECT3DTEXTURE9 _texture)
{
    Initialize(_device,_texture);
}

Sprite::~Sprite()
{
    sprite->Release();
}

// Initializes values and creates the sprite
void Sprite::Initialize(LPDIRECT3DDEVICE9 _device, LPDIRECT3DTEXTURE9 _texture)
{
    Initialize();

    // Set our variables
    texture = _texture;

    // If we don't succeed throw an error so we know things
    // got ****ed up somehow
    if (!SUCCEEDED(D3DXCreateSprite(_device, &sprite)))
    {
        throw("Sprite creation failed");
    }
    SetCenter();
    SetSourceRect();
}

// Sets initial values for the sprite
void Sprite::Initialize()
{
    // Sets variable to default  
}

这就是我所说的。

Testground.h

#ifndef TESTGROUND_H
#define TESTGROUND_H
#include "Console.h"
#include "Log.h"
#include "sprite.h"
#include <string>
#include "Animation.h"
#include <map>
#include <vector>

class TestGround
{
private:
    std::vector<Sprite> sprites;

这里有一点Testground.cpp

Testground.cpp

#include "TestGround.h"


    TestGround::TestGround(LPDIRECT3DDEVICE9 _device)
    {

        sprites.emplace_back(_device, tBank["TestTexture"]);

1 个答案:

答案 0 :(得分:2)

更新:我的原始答案是关于C ++语言的东西和复制。现在我看到底层对象是一个COM对象,因此有更多方法可以解决它。

问题是你的Sprite类拥有一个指向Direct3D精灵的原始指针,这是一个COM对象。如果复制了Sprite的实例,则现在有两个对象具有指向同一COM对象的指针。如果其中一个Sprite被破坏,它会破坏COM对象,剩下的Sprite会留下一个悬空指针。

当向量需要增长时,可能会发生此复制。向量分配空间并将对象复制(或移动)到新空间,并销毁原始副本。 (移动是C ++ 11的一项功能,您需要做一些工作来支持它。)

COM对象是引用计数的,因此在最后一次发布之前不应销毁它们。但是引用计数负担落在你身上。复制Sprite时,相应D3DXSprite对象中的引用计数需要递增。

选项1:您可以通过实现Sprite复制构造函数并让它在指针上调用AddRef来直接执行此操作。

Sprite::Sprite(const Sprite &_copy) :
  center(_copy.center),
  color(_copy.color),
  // blah blah blah
  sprite(_copy.sprite)
{
  sprite->AddRef();
}

选项2:使用智能指针管理引用计数。我发现来自ATL::CComPtr的{​​{1}}对此非常有效。

<atlbase.h>

现在您甚至不必实现复制构造函数。编译器会通过复制每个成员来为您完成。复制CComPtr时,会为您增加引用计数。当你的Sprite被销毁时,CComPtr会减少引用计数(因此你不再需要你的~Sprite析构函数 - 或者,如果你这样做,它不再需要调用sprite-&gt; Release())。

我的原始答案如下,这些也是有效选项。

选项1.如果您正在使用C ++ 11,请实现移动构造函数并为Sprite移动赋值运算符,然后将Sprite直接放入std :: vector中,就像您现在所做的那样。

选项2.使您的Sprite不可复制(通过使用#include <atlbase.h> class Sprite { // blah blah blah private: ATL::CComPtr<ID3DXSprite> sprite; }; 声明复制构造函数和赋值运算符,或者将它们声明为私有而不实现它们)。然后让你的向量保持智能指针指向你的精灵(例如,= delete)。

选项3.使您的Sprite复制构造函数和复制赋值运算符执行深层复制 - 即创建一个新的std::vector<std::shared_ptr<Sprite>>,其设置方式与源对象中的相同。使用这种方法,您可以将它们直接放入std :: vector。