Проблемы производного класса C ++ - PullRequest
1 голос
/ 18 сентября 2008

Я делаю игру на C ++ и у меня проблемы с производным классом. У меня есть базовый класс с именем GameScreen, который имеет функцию vitrual void draw () без операторов. У меня также есть производный класс с именем MenuScreen, который также имеет виртуальную функцию void draw () и производный класс от MenuScreen с именем TestMenu, который также имеет функцию void draw (). В моей программе у меня есть список GameScreens, через которые проходит итератор GameScreen, вызывая каждую функцию GameScreens draw ().

Проблема в том, что я поместил объект TestMenu в список GameScreen. Вместо итератора, вызывающего функцию draw () класса TestMenu, он вызывает функцию draw () класса GameScreen. Кто-нибудь знает, как я мог бы вызвать функцию draw () TestMenu вместо функции в 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 ]

9 голосов
/ 18 сентября 2008

Если gsScreens - это список объектов вместо списка указателей (как подсказывает ваш код), то вы не сохраняете то, что, по вашему мнению, храните в нем.

Происходит следующее: вместо того, чтобы помещать TestMenu в список, вы фактически создаете новый MenuScreen с помощью созданного компилятором конструктора копирования и помещаете этот MenuScreen в список.

C ++ полиморфен через указатели, поэтому, если у вас нет указателя, вы не получите полиморфное поведение.

1 голос
/ 18 сентября 2008

Чтобы получить полиморфное поведение, к которому вы стремитесь, и в то же время использовать std::vector<>, вы должны хранить указатели на тип базового класса в векторе вместо хранения значений . Кроме того, вы должны помнить, чтобы освободить их память, прежде чем вектор выйдет из области видимости.

Например:

#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())
};
0 голосов
/ 18 сентября 2008

Boost (www.boost.org, библиотека, которую я бы порекомендовал любому, кто использует C ++), предоставляет базовый класс без возможности копирования, который делает именно это; вам не нужен ужасный макрос таким образом.

0 голосов
/ 18 сентября 2008

Курт абсолютно правильно, но я просто хотел бы добавить немного больше информации.

Эта проблема (хранение объектов базового класса, а не указателей) иногда называется "нарезкой".

Также я обычно использую следующий макрос:

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

Затем вы помещаете это где-то в определение вашего класса:

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

Если другой класс попытается скопировать объект, вы получите ошибку компилятора (потому что методы объявлены закрытыми). Если сам класс попытается скопировать объект, вы получите ошибку компоновщика (потому что методы не имеют реализации).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...