带有列表问题的 C++ 多态性

C++ Polymorphism with Lists issue

首先感谢您抽出宝贵的时间,对于这么长的时间我深表歉意post,但我找不到任何其他方法来缩短它,而且对我来说也是英语!有什么不明白的就问吧^^。希望你能找到错误,因为这让我发疯。

我目前正在学习 DirectX 11 并且我正在从该网站制作 This Little Game 但应用 OOP 和 DirectX 11 而不是 9 只是从该项目中获取某些东西。

好的,现在你已经了解了一些上下文,这就是问题所在。

我制作了一个名为 GameObject 的抽象 class,它封装了与渲染有关的所有功能,例如存储图像、动画、帧之间的过渡等。使用此 GameObject class定义将在我的游戏中交互的所有其他对象。

游戏对象Class

////////////////////////////////////////////////////////////////////////////////
// Filename: GameObject.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _GAME_OBJECT_H_
#define _GAME_OBJECT_H_

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "Sprite.h"
#include "InputHandler.h"
#include "Timer.h"

class GameObject
{
public:
    GameObject();
    GameObject(const GameObject& other);
    ~GameObject();

    virtual bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen) = 0;
    bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen, WCHAR* spriteFileName, Bitmap::DimensionType bitmap, Bitmap::DimensionType sprite, int numberOfFramesAcross, int initialFrame, bool useTimer);
    virtual void Shutdown();
    virtual bool Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix);

    void Move();
    void Move(const D3DXVECTOR2 vector);

    virtual void Frame(const InputHandler::ControlsType& controls);
    void SortFrameArray(const int* framesOrder, int size);

    void SetPosition(const POINT& position);
    const POINT GetPosition();

    void SetVelocity(const D3DXVECTOR2& velocity);
    const D3DXVECTOR2 GetVelocity();

    void SetStatus(const bool status);
    bool GetStatus();

    float GetMovementDelayTime();
    void ResetMovementDelayTime();

    float GetAnimationDelayTime();
    void ResetAnimationDelayTime();

    //Both of this objects i think i'll remove them from this class. I don't think they belong here.
    ID3D11Device* GetDevice();
    HWND GetHWND();

    Sprite* GetSprite();

protected:
    ID3D11Device* m_device;
    HWND m_hwnd;
    Sprite* m_Sprite;
    Timer* m_Timer;
    POINT m_position;
    D3DXVECTOR2 m_velocity;
    bool m_active;
    float m_movementDelay;
    float m_animationDelay;
};
#endif

Cpp

////////////////////////////////////////////////////////////////////////////////
// Filename: GameObject.cpp
////////////////////////////////////////////////////////////////////////////////
#include "GameObject.h"

GameObject::GameObject()
{
    this->m_Sprite = nullptr;
    this->m_Timer = nullptr;
    this->m_movementDelay = 0.0f;
    this->m_animationDelay = 0.0f;
}

GameObject::GameObject(const GameObject& other)
{
}

GameObject::~GameObject()
{
}

bool GameObject::Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen, WCHAR* spriteFileName, Bitmap::DimensionType bitmap, Bitmap::DimensionType sprite, int numberOfFramesAcross, int initialFrame, bool useTimer)
{
    bool result;

    this->m_device = device;
    this->m_hwnd = hwnd;

    this->m_Sprite = new Sprite();
    if (!this->m_Sprite)
    {
        return false;
    }

    result = this->m_Sprite->Initialize(device, hwnd, screen, spriteFileName, bitmap, sprite, numberOfFramesAcross, initialFrame);
    if (!result)
    {
        return false;
    }

    if (useTimer)
    {
        this->m_Timer = new Timer();
        if (!this->m_Timer)
        {
            return false;
        }

        result = this->m_Timer->Initialize();
        if (!result)
        {
            return false;
        }
    }
    return true;
}

void GameObject::Shutdown()
{
    SAFE_SHUTDOWN(this->m_Sprite);
    SAFE_DELETE(this->m_Timer);
}

bool GameObject::Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
    return this->m_Sprite->Render(deviceContext, this->m_position, wordMatrix, viewMatrix, projectionMatrix);
}

void GameObject::Move()
{
    this->m_position.x += this->m_velocity.x;
    this->m_position.y += this->m_velocity.y;
}

void GameObject::Move(const D3DXVECTOR2 vector)
{
    this->m_position.x += vector.x;
    this->m_position.y += vector.y;
}

void GameObject::Frame(const InputHandler::ControlsType& controls)
{
    if (this->m_Timer)
    {
        this->m_Timer->Frame();

        this->m_movementDelay += this->m_Timer->GetTime();
        this->m_animationDelay += this->m_Timer->GetTime();
    }
}

void GameObject::SortFrameArray(const int* framesOrder, int size)
{
    this->m_Sprite->SortFrameArray(framesOrder, size);
}

void GameObject::SetPosition(const POINT& position)
{
    this->m_position = position;
}

const POINT GameObject::GetPosition()
{
    return this->m_position;
}

void GameObject::SetVelocity(const D3DXVECTOR2& velocity)
{
    this->m_velocity = velocity;
}

const D3DXVECTOR2 GameObject::GetVelocity()
{
    return this->m_velocity;
}

void GameObject::SetStatus(const bool status)
{
    this->m_active = status;
}

bool GameObject::GetStatus()
{
    return this->m_active;
}

Sprite* GameObject::GetSprite()
{
    return this->m_Sprite;
}

float GameObject::GetAnimationDelayTime()
{
    return this->m_animationDelay;
}

void GameObject::ResetMovementDelayTime()
{
    this->m_movementDelay = 0.0f;
}

float GameObject::GetMovementDelayTime()
{
    return this->m_animationDelay;
}

void GameObject::ResetAnimationDelayTime()
{
    this->m_animationDelay = 0.0f;
}

ID3D11Device* GameObject::GetDevice()
{
    return this->m_device;
}

HWND GameObject::GetHWND()
{
    return this->m_hwnd;
}

然后我制作了派生的 class Fighter 代表我的宇宙飞船并且有一个我认为与问题无关的 FighterFlame,以及指向 Bullet 指针的指针列表(m_Bullets) 这将是从飞船中射出的子弹。

////////////////////////////////////////////////////////////////////////////////
// Filename: Fighter.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _FIGHTER_H_
#define _FIGHTER_H_

//////////////
// INCLUDES //
//////////////
#include <list>

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "GameObject.h"
#include "Bullet.h"
#include "FighterFlame.h"

class Fighter : public GameObject
{
public:
    Fighter();
    Fighter(const Fighter& other);
    ~Fighter();

    virtual bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen) override;
    virtual void Shutdown();
    virtual bool Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix) override;

    virtual void Frame(const InputHandler::ControlsType& controls) override;

private:
    void GenerateTriBullet();
    void ValidateBulletsBounds();

private:
    int m_life;
    int m_lives;

    FighterFlame* m_FighterFlame;
    std::list<Bullet**> m_Bullets;

    const int SHIP_SPEED = 3;
    const float MOVEMENT_DELAY = 16.0f;
    const float ANIMATION_DELAY = 20.0f;
    const float SHOOT_DELAY = 30.0f;
};
#endif

Cpp

////////////////////////////////////////////////////////////////////////////////
// Filename: Fighter.cpp
////////////////////////////////////////////////////////////////////////////////
#include "Fighter.h"

Fighter::Fighter() : GameObject()
{
    this->m_life = 100;
    this->m_lives = 3;

    this->m_FighterFlame = nullptr;
}

Fighter::Fighter(const Fighter& other)
{
}

Fighter::~Fighter()
{
}

bool Fighter::Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen)
{
    bool result;

    this->m_life = 100;
    this->m_lives = 3;

    result = GameObject::Initialize(device, hwnd, screen, L"Fighter.dds", Bitmap::DimensionType{ 1152, 216 }, Bitmap::DimensionType{ 144, 108 }, 8, 7, true);
    if (!result)
    {
        MessageBox(hwnd, L"Could not initialize Fighter", L"Error", MB_OK);
        return false;
    }

    this->m_position = POINT{ 0, 0 };

    int order[16] = { 7, 6, 5, 4, 3, 2, 1, 0, 8, 9, 10, 11, 12, 13, 14, 15 };
    GameObject::SortFrameArray(order, 16);

    this->m_FighterFlame = new FighterFlame();
    if (!this->m_FighterFlame)
    {
        return false;
    }

    result = this->m_FighterFlame->Initialize(device, hwnd, screen);
    if (!result)
    {
        MessageBox(hwnd, L"Could not initialize FighterFlame", L"Error", MB_OK);
        return false;
    }

    return true;
}

bool Fighter::Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX wordMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
    bool result;

    result = GameObject::Render(deviceContext, wordMatrix, viewMatrix, projectionMatrix);
    if (!result)
    {
        return false;
    }

    result = this->m_FighterFlame->Render(deviceContext, wordMatrix, viewMatrix, projectionMatrix);
    if (!result)
    {
        return false;
    }

    for (Bullet** bullet : this->m_Bullets)
    {
        if (bullet)
        {
            result = (*bullet)->Render(deviceContext, wordMatrix, viewMatrix, projectionMatrix);
            if (!result)
            {
                return false;
            }
        }
    }

    return true;
}

void Fighter::Shutdown()
{
    GameObject::Shutdown();
    SAFE_SHUTDOWN(this->m_FighterFlame);

    for (Bullet** bullet : this->m_Bullets)
    {
        SAFE_SHUTDOWN(*bullet);
    }
    this->m_Bullets.clear();
}

void Fighter::Frame(const InputHandler::ControlsType& controls)
{
    GameObject::Frame(controls);
    this->m_FighterFlame->SetPosition(POINT{ this->m_position.x - 26, this->m_position.y + 47});
    this->m_FighterFlame->Frame(controls);

    for (Bullet** bullet : this->m_Bullets)
    {
        (*bullet)->Frame(controls);
    }

    if (GameObject::GetMovementDelayTime() > MOVEMENT_DELAY)
    {
        if (controls.up ^ controls.down)
        {
            if (controls.up)
            {
                if (GameObject::GetPosition().y > 0)
                {
                    GameObject::Move(D3DXVECTOR2(0, -SHIP_SPEED));
                }

                if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
                {
                    GameObject::GetSprite()->IncrementFrame();
                    GameObject::ResetAnimationDelayTime();
                }
            }
            else if (controls.down)
            {
                if (GameObject::GetPosition().y < (GameObject::GetSprite()->GetBitmap()->GetScreenDimensions().height - GameObject::GetSprite()->GetBitmap()->GetBitmapDimensions().height))
                {
                    GameObject::Move(D3DXVECTOR2(0, SHIP_SPEED));
                }
                if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
                {
                    GameObject::GetSprite()->DecrementFrame();
                    GameObject::ResetAnimationDelayTime();
                }
            }
        }
        else
        {
            if (GameObject::GetSprite()->GetCurrentFrame() > (GameObject::GetSprite()->GetAmountOfFrames() / 2))
            {
                if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
                {
                    GameObject::GetSprite()->DecrementFrame();
                    GameObject::ResetAnimationDelayTime();
                }
            }
            if (GameObject::GetSprite()->GetCurrentFrame() < (GameObject::GetSprite()->GetAmountOfFrames() / 2))
            {
                if (GameObject::GetAnimationDelayTime() > ANIMATION_DELAY)
                {
                    GameObject::GetSprite()->IncrementFrame();
                    GameObject::ResetAnimationDelayTime();
                }
            }
        }
        if (controls.right ^ controls.left)
        {
            if (controls.right)
            {
                if (GameObject::GetPosition().x < (GameObject::GetSprite()->GetBitmap()->GetScreenDimensions().width - GameObject::GetSprite()->GetBitmap()->GetBitmapDimensions().width))
                {
                    GameObject::Move(D3DXVECTOR2(SHIP_SPEED, 0));
                }
            }
            else if (controls.left)
            {
                if (GameObject::GetPosition().x > 0)
                {
                    GameObject::Move(D3DXVECTOR2(-SHIP_SPEED, 0));
                }
            }
        }
        GameObject::ResetMovementDelayTime();
    }

    if (controls.spaceBar)
    {
        Fighter::GenerateTriBullet();
    }
    Fighter::ValidateBulletsBounds();
}

void Fighter::GenerateTriBullet()
{
    Bullet* upBullet = new Bullet();
    upBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    upBullet->SetVelocity(D3DXVECTOR2(20, 2));
    upBullet->SetPosition(GameObject::GetPosition());
    upBullet->Move();
    this->m_Bullets.push_back(&upBullet);

    Bullet* middleBullet = new Bullet();
    middleBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    middleBullet->SetVelocity(D3DXVECTOR2(20, 0));
    middleBullet->SetPosition(GameObject::GetPosition());
    middleBullet->Move();
    this->m_Bullets.push_back(&middleBullet);

    Bullet* downBullet = new Bullet();
    downBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    downBullet->SetVelocity(D3DXVECTOR2(20, -2));
    downBullet->SetPosition(GameObject::GetPosition());
    downBullet->Move();
    this->m_Bullets.push_back(&downBullet);
}

void Fighter::ValidateBulletsBounds()
{
    for (std::list<Bullet**>::iterator it = this->m_Bullets.begin(); it != this->m_Bullets.end(); it++)
    {
        if ((*(*(&(it)._Ptr->_Myval)))->GetPosition().x > GameObject::GetSprite()->GetBitmap()->GetScreenDimensions().width)
        {
            SAFE_SHUTDOWN(**it);
            this->m_Bullets.erase(it);
        }
    }
}

最后是有问题的子弹 class,它也派生自 GameObject,代表 space飞船可以发射的子弹。

////////////////////////////////////////////////////////////////////////////////
// Filename: Bullet.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _BULLET_H_
#define _BULLET_H_

///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "GameObject.h"

class Bullet : public GameObject
{
public:
    Bullet();
    Bullet(const Bullet& other);
    ~Bullet();

    virtual bool Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen) override;

    virtual void Frame(const InputHandler::ControlsType& controls) override;

private:
    const float MOVEMENT_DELAY = 16.0f;
};
#endif

Cpp

////////////////////////////////////////////////////////////////////////////////
// Filename: Bullet.cpp
////////////////////////////////////////////////////////////////////////////////
#include "Bullet.h"

Bullet::Bullet() : GameObject()
{
}

Bullet::Bullet(const Bullet& other)
{
}

Bullet::~Bullet()
{
}

bool Bullet::Initialize(ID3D11Device* device, HWND hwnd, Bitmap::DimensionType screen)
{
    bool result;

    result = GameObject::Initialize(device, hwnd, screen, L"Bullet.dds", Bitmap::DimensionType{ 18, 3 }, Bitmap::DimensionType{ 18, 3 }, 1, 0, true);
    if (!result)
    {
        return false;
    }

    return true;
}

void Bullet::Frame(const InputHandler::ControlsType& controls)
{
    GameObject::Frame(controls);
    if (GameObject::GetMovementDelayTime() > MOVEMENT_DELAY)
    {
        GameObject::Move();
    }
}

还有问题:

当 Gameloop 处于 运行ning 并且我按下 space 栏时,会发生这种情况

// this if is from Fighter::Frame
if (controls.spaceBar)
{
    Fighter::GenerateTriBullet();
}
Fighter::ValidateBulletsBounds();

进入GenerateTriBullet方法,在m_Bullets列表中存储3颗子弹

void Fighter::GenerateTriBullet()
{
    Bullet* upBullet = new Bullet();
    upBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    upBullet->SetVelocity(D3DXVECTOR2(20, 2));
    upBullet->SetPosition(GameObject::GetPosition());
    upBullet->Move();
    this->m_Bullets.push_back(&upBullet);

    Bullet* middleBullet = new Bullet();
    middleBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    middleBullet->SetVelocity(D3DXVECTOR2(20, 0));
    middleBullet->SetPosition(GameObject::GetPosition());
    middleBullet->Move();
    this->m_Bullets.push_back(&middleBullet);

    Bullet* downBullet = new Bullet();
    downBullet->Initialize(this->m_FighterFlame->GetDevice(), this->m_FighterFlame->GetHWND(), this->m_FighterFlame->GetSprite()->GetBitmap()->GetScreenDimensions());
    downBullet->SetVelocity(D3DXVECTOR2(20, -2));
    downBullet->SetPosition(GameObject::GetPosition());
    downBullet->Move();
    this->m_Bullets.push_back(&downBullet);
}

当它离开方法时,我检查了列表,项目符号仍然存在,就像在进入 ValidateBulletsBound 之前一样,但是一旦它进入方法并且在执行之前ANYTHING,列表中的项目符号完全消失了,我的意思是,m_Bullets 列表仍然有三个对象,但是碰巧它们都是NULL。

为了更好地解释我自己,我想做的是每次我按下 space-bar 3 颗子弹出现在屏幕上,我试图通过询问是否 space-bar值为true,在m_Bullet列表中添加3个Bullets,然后验证列表中的bullets还在屏幕之间space , 否则删除它。 But as you can see, i successfully store the bullets on the list and as soon as i enter to validate, they are gone... poof!

我不知道为什么会发生这种情况,它们是 class 的不同实例,它们之间不共享任何东西(从内存方面来说),没有共享静态方法或指针到那时,即使他们愿意,考虑到他们只是进入另一种方法并且在 "entering the another method" 部分的中间或其他任何地方都没有进行任何操作,所以这应该不是问题。它们甚至处于相同的 class、相同的上下文中,没有复杂的操作或泄漏(据我所知)。真不知道怎么回事!

我想在结束时承认存在一些严重的设计问题,例如 GenerateTriBullet 上的问题,以及我没有使用矩阵来移动对象这一事实。我只是想先完成它(这是我在 DirectX 上制作的第一款游戏,顺便说一句真的退出了!!!),然后当我能看到大局时,开始把所有东西都放在它所属的地方。另外,我如何从列表迭代器中获取值,我读到它是 (*it) 用于简单值,但我有一个指向指针的指针,所以我认为它会是 **it,但它总是解析为nullptr.

我真的希望你能帮助我。 这是项目,如果你觉得你不明白并且想更进一步。你只需要运行这个项目,就会出现一个小黑window的飞船,在Fighterclass的第182行打个断点然后按spacebar游戏 window,然后从那里查看 m_Bullets 在离开 GenerateTriBullet 并进入 ValidateBulletsBounds.[=21 时会发生什么=]

THANK YOU!

一个明确的问题:

m_Bullets 是一个 list<Bullet**>。当您添加到它时,您正在添加局部变量的地址,例如

Bullet* upBullet = new Bullet();
...
this->m_Bullets.push_back(&upBullet);

使用此方法 returns,从 &upBullet 表达式存储的地址不再有效,因为该变量不再存在。

我想你的意思是将 m_Bullets 设为 list<Bullet*>,然后添加:

Bullet* upBullet = new Bullet();
...
this->m_Bullets.push_back(upBullet);

我认为最好的解决方案是让 list 处理内存管理,只将 m_Bullets 作为 list<Bullet>,然后:

this->m_Bullets.emplace_back();

然而,这可能需要重新考虑一些多态性。