1

我正在用 C++ 制作游戏,但我的派生类有问题。我有一个名为 GameScreen 的基类,它有一个没有语句的虚拟 void draw() 函数。我还有一个名为 MenuScreen 的派生类,它也有一个虚拟 void draw() 函数和一个来自 MenuScreen 的名为 TestMenu 的派生类,它也有一个 void draw() 函数。在我的程序中,我有一个 GameScreen 列表,我有一个 GameScreen 迭代器通过调用每个 GameScreens draw() 函数。

问题是我在 GameScreen 列表中放置了一个 TestMenu 对象。不是迭代器调用 TestMenu 的 draw() 函数,而是调用 GameScreen 类的 draw() 函数。有谁知道我如何调用 TestMenu 的 draw() 函数而不是 GameScreen 中的函数。

这是功能:

// Tell each screen to draw itself.
//gsElement is a GameScreen iterator
    //gsScreens is a list of type GameScreen
void Draw()
{
    for (gsElement = gsScreens.begin(); gsElement != gsScreens.end(); gsElement++)
    {
        /*if (gsElement->ssState == Hidden)
            continue;*/

        gsElement->Draw();
    }
}   

这是我的课程的副本:

class GameScreen {
public:
    string strName;
    bool bIsPopup;
    bool bOtherScreenHasFocus;
    ScreenState ssState;
    //ScreenManager smScreenManager;

    GameScreen(string strName){
        this->strName = strName;
    }

    //Determine if the screen should be drawn or not
    bool IsActive(){
        return !bOtherScreenHasFocus && 
            (ssState == Active);
    }

    //------------------------------------
    //Load graphics content for the screen
    //------------------------------------
    virtual void LoadContent(){
    }

    //------------------------------------
    //Unload content for the screen
    //------------------------------------
    virtual void UnloadContent(){
    }

    //-------------------------------------------------------------------------
    //Update changes whether the screen should be updated or not and sets
    //whether the screen should be drawn or not.
    //
    //Input:
    //  bOtherScreenHasFocus - is used set whether the screen should update
    //  bCoveredByOtherScreen - is used to set whether the screen is drawn or not
    //-------------------------------------------------------------------------
    virtual void Update(bool bOtherScreenHasFocus, bool bCoveredByOtherScreen){
        this->bOtherScreenHasFocus = bOtherScreenHasFocus;

        //if the screen is covered by another than change the screen state to hidden
        //else set the screen state to active
        if(bCoveredByOtherScreen){
            ssState = Hidden;
        }
        else{
            ssState = Active;
        }
    }

    //-----------------------------------------------------------
    //Takes input from the mouse and calls appropriate actions
    //-----------------------------------------------------------
    virtual void HandleInput(){
    }

    //----------------------
    //Draw content on screen
    //----------------------
    virtual void Draw(){
    }

    //--------------------------------------
    //Deletes screen from the screen manager
    //--------------------------------------
    void ExitScreen(){
        //smScreenManager.RemoveScreen(*this);
    }
};

class MenuScreen: public GameScreen{
public:
    vector <BUTTON> vbtnMenuEntries; 

    MenuScreen(string strName):GameScreen(strName){
    }

    virtual void Update(bool bOtherScreenHasFocus, bool bCoveredByOtherScreen){
        GameScreen::Update(bOtherScreenHasFocus, bCoveredByOtherScreen);

        for(unsigned int i = 0; i < vbtnMenuEntries.size(); i++){
            vbtnMenuEntries[i].IsPressed();
        }
    }

    virtual void Draw(){
        GameScreen::Draw();

        for(unsigned int i = 0; i < vbtnMenuEntries.size(); i++)
            vbtnMenuEntries[i].Draw();
    }

};

class testMenu : public MenuScreen{
public:
    vector<OBJECT> test;
    //OBJECT background3();
//  OBJECT testPic(512, 384, buttonHover.png, 100, 40, 100, 40);
//  BUTTON x(256, 384, buttonNormal.png, buttonHover.png, buttonPressed.png, 100, 40, test());
    bool draw;

    testMenu():MenuScreen("testMenu"){
        OBJECT background3(1, 1, 0, TEXT("background.png"), 1, 1, 1024, 768);
        OBJECT testPic(512, 384,0, TEXT("buttonHover.png"), 1, 1, 100, 40);
        test.push_back(background3);
        test.push_back(testPic);
        //background3.Init(int xLoc, int yLoc, int zLoc, LPCTSTR filePath, int Rows, int Cols, int Width, int Height)
        //test.push_back(background3);
    //  vbtnMenuEntries.push_back(x);
        draw = false;
    }

    void Update(bool bOtherScreenHasFocus, bool bCoveredByOtherScreen){
        MenuScreen::Update(bOtherScreenHasFocus, bCoveredByOtherScreen);
        //cout << "X" << endl;
        /*if(MouseLButton == true){
            testMenu2 t;
            smManager.AddScreen(t);
        }*/
    }

    void Draw(){
        //background3.Draw();
        test[0].Draw();
        test[1].Draw();
        MenuScreen::Draw();
    ///*if(draw){*/
    //  testPic.Draw();
    //}
}

/*void test(){
    draw = true;
}*/

};
4

4 回答 4

9

如果 gsScreens 是对象列表而不是指针列表(如您的代码所示),那么您不会存储您认为存储在其中的内容。

发生的事情是 -- 不是将 TestMenu 放入列表中,而是使用编译器生成的复制构造函数构造一个新的 MenuScreen 并将此 MenuScreen 放入列表中。

C++ 通过指针实现多态,所以如果你没有指针,你就不会得到多态行为。

于 2008-09-17T23:18:40.447 回答
1

要获得您所追求的多态行为并同时使用 a std::vector<>,您必须将指向基类类型的指针存储在向量中,而不是存储values。此外,您必须记住在向量超出范围之前释放它们的内存。

例如:

#include <vector>
#include <algorithm>

struct Base
{
    virtual void Foo() = 0;
    virtual ~Base() { }
};

struct Derived1 : public Base
{
    void Foo() { }
};

struct Derived2 : public Base
{
    void Foo() { }
};

struct delete_ptr
{
    template <typename T>
    void operator()(T& p)
    {
        delete p;
        p = 0;
    }
};

int wmain(int, wchar_t*[])
{
    std::vector<Base*> items;
    items.push_back(new Derived1);
    items.push_back(new Derived2);

    Base& first = items.front();
    first.Foo(); // Will boil down to Derived1::Foo().

    Base& last = items.back();
    last.Foo(); // Will boil down to Derived2::Foo().

    std::for_each(items.begin(), items.end(), delete_ptr())
};
于 2008-09-18T06:57:47.613 回答
0

Curt是绝对正确的,但我只想提供更多信息。

这个问题(存储基类对象,而不是指针)有时被称为“切片”。

另外,我倾向于使用以下宏:

#define DISALLOW_COPYING(X) \
    private: \
        X(const X &); \
        const X& operator= (const X& x)

然后你把它放在你的类定义中的某个地方:

class Foo {
    // ...
    DISALLOW_COPYING(Foo);
};

如果另一个类尝试复制该对象,您将收到编译器错误(因为这些方法被声明为私有)。如果类本身尝试复制对象,您将收到链接器错误(因为方法没有实现)。

于 2008-09-18T07:05:11.450 回答
0

Boost(www.boost.org,一个我推荐任何使用 C++ 编码的人的库)提供了一个不可复制的基类,可以做到这一点;这样你就不需要丑陋的宏了。

于 2008-09-18T16:51:04.363 回答