C ++派生类问题
我正在用C ++进行游戏,派生类遇到了问题。我有一个称为GameScreen的基类,该基类具有一个无语句的虚拟void draw()函数。我也有一个名为MenuScreen的派生类,它也有一个虚拟的void draw()函数,还有一个从MenuScreen的名为TestMenu的派生类,它也有一个虚空draw()函数。在我的程序中,我有一个GameScreens列表,我有一个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; }*/ };
解决方案
如果gsScreens是对象列表而不是指针列表(如代码所示),那么我们将不会存储我们认为存储在其中的内容。
发生的是-我们不是在将TestMenu放入列表中,而是在实际上使用编译器生成的复制构造函数构造一个新的MenuScreen并将此MenuScreen放入列表中。
C ++通过指针是多态的,因此,如果没有指针,我们将不会得到多态的行为。
为了获得我们要遵循的多态行为,同时使用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()) };
Curt是绝对正确的,但是我想向它提供更多信息。
这个问题(存储基类对象,而不是指针)有时被称为"切片"。
另外,我倾向于使用以下宏:
#define DISALLOW_COPYING(X) \ private: \ X(const X &); \ const X& operator= (const X& x)
然后将其放在类定义中的某个位置:
class Foo { // ... DISALLOW_COPYING(Foo); };
如果另一个类尝试复制该对象,则会出现编译器错误(因为方法被声明为私有)。如果类本身尝试复制对象,则会出现链接器错误(因为这些方法没有实现)。
Boost(www.boost.org,我建议使用C ++进行编码的任何人都建议使用的库)提供了一个不可复制的基类,它可以做到这一点。我们不需要那种丑陋的宏。