views:

860

answers:

4

I am making a game in C++ and am having problems with my derived class. I have a base class called GameScreen which has a vitrual void draw() function with no statements. I also have a derived class called MenuScreen which also has a virtual void draw() function and a derived class from MenuScreen called TestMenu which also has a void draw() function. In my program I have a list of GameScreens that I have a GameScreen iterator pass through calling each GameScreens draw() function.

The issue is that I have placed a TestMenu object on the GameScreen list. Instead of the iterator calling the draw() function of TestMenu it is calling the draw() function of the GameScreen class. Does anyone know how I could call the draw() function of TestMenu instead of the one in GameScreen.

Here is the function:

// 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();
    }
}   

Here are a copy of my classes:

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;
}*/

};
+9  A: 

If gsScreens is a list of objects instead of a list of pointers (as your code suggests), then you're not storing what you think you're storing in it.

What's happening is that -- instead of putting a TestMenu into the list, you're actually constructing a new MenuScreen using the compiler-generated copy constructor and putting this MenuScreen into the list.

C++ is polymorphic through pointers, so if you don't have a pointer you won't get polymorphic behavior.

Curt Hagenlocher
Curts suggestion sounds correct change gsScreens to be a list of type GameScreen*.Polymorphism requires pointers or references to objects.
morechilli
Avoid such accidental copying by declaring the copy constructor and operator= private. This supresses the defaults, which are there for compatibility with C structs. If you want a class to be copyable (rare), then implement both. Make sure they interact correctly with the destructor.
Steve Jessop
C++ is also polymorphic through references.
OJ
References are just poorly-disguised pointers :P
Curt Hagenlocher
That's an implementation detail ;) In the semantics of the language, references and pointers are different things and hence both should be mentioned when you talk about polymorphism. I'm not saying you're wrong, I was just adding to your almost correct answer ;)
OJ
+1  A: 

To get the polymorphic behavior you're after and at the same time use a std::vector<>, you must store pointers to the base class type in the vector, instead of storing values. Also, you must remember to free their memory before the vector goes out of scope.

For instance:

#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())
};
Johann Gerell
A: 

Curt is absolutely correct, but I'd just like to throw a little more information at it.

This problem (storing base-class objects, rather than pointers) is sometimes called "slicing".

Also, I tend to make use of the following macro:

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

Then you put this somewhere in your class definition:

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

If another class attempts to copy the object, you'll get a compiler error (because the methods are declared private). If the class itself attempts to copy the object, you'll get a linker error (because the methods have no implementation).

Roger Lipscombe
A: 

Boost (www.boost.org, a library I would recommend anyone coding in C++ use) provides a noncopyable base class that does exactly that; you don't need an ugly macro that way.

coppro