2008-09-17 2 views
1

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

Проблема в том, что я поместил объект 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; 
}*/ 

}; 
+0

Ответ Курта правильный. Вы должны сделать игру DrawScreen чистой виртуальной, чтобы вы * не могли * иметь этот тип ошибки. – moswald 2008-09-18 17:00:18

ответ

9

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

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

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

+0

Curts suggestion звучит правильно. GsScreens - список типов GameScreen *. Полиморфизм требует указателей или ссылок на объекты. – morechilli 2008-09-17 23:43:01

1

Чтобы получить полиморфное поведение вы после этого, и в то же время использовать 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

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

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

Кроме того, я, как правило, использовать следующий макрос:

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

Тогда вы положили это где-то в своем определении класса:

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

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

0

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

Смежные вопросы