了解C ++中的抽象

时间:2014-02-06 01:36:26

标签: c++ visual-c++ inheritance initialization

从Java到C ++我试图通过面向对象来理解抽象。

将这个放到一个实际的例子中,我正在开发一个使用SFML库进行图形处理的小游戏。然而,这个问题与此无关,只需将其视为背景信息。无论如何,游戏的工作方式是处理许多不同的状态。在这种情况下2:

  1. 菜单状态:绘制游戏菜单,游戏将从此处开始。

  2. 游戏状态:此状态控制游戏,将更新实体并绘制它们。

  3. 为了做到这一点,我创建了以下类:

    GameStateManager.h

    #ifndef GAMESTATEMANAGER_H
    #define GAMESTATEMANAGER_H
    
    #include <SFML/Graphics.hpp>
    #include <iostream>
    #include "GameState.h"
    
    class GameStateManager
    {
    public:
        // Constructor
        GameStateManager();
        // State variables
        static const int NUMGAMESTATES = 2;
        static const int MENUSTATE = 0;
        static const int GAMESTATE = 1;
        // Public Functions
        void set_state(int state);
        void update();
        void draw(sf::RenderWindow &win);
        void input(sf::Event event);
    
    private:
        // Array of gamestates
        GameState game_states[];
        // The current state
        int current_state;
        // Private functions
        void load_state(int state);
        void unload_state(int state);
    };
    
    #endif
    

    GameState.h

    #ifndef GAMESTATE_H
    #define GAMESTATE_H
    
    #include <iostream>
    #include <SFML/Graphics.hpp>
    #include "GameStateManager.h"
    
    class GameState
    {
    protected:
        GameStateManager gsm;
    public:
        virtual void init() = 0;
        virtual void update() = 0;
        virtual void draw(sf::RenderWindow &win) = 0;
        virtual void input(sf::Event event) = 0;
    };
    
    #endif
    

    现在您可能已经注意到Game State Manager中的GameStates数组?这提供了一个我不理解的错误:零大小的数组。这是否意味着需要在头文件中进行初始化?此外编译器提到不允许使用Array of Abstract类?

    第二个问题是抽象GameState类中的字段gsm无法识别并引发另一个错误:缺少类型说明符。

    现在进一步复杂化我有以下类:MenuState。这个类旨在扩展GameState。

    MenuState.h

    #ifndef MENUSTATE_H
    #define MENUSTATE_H
    
    #include "GameState.h"
    
    class MenuState: public GameState
    {
    public:
        MenuState(GameStateManager gsm);
        void init();
        void update();
        void draw(sf::RenderWindow &win);
        void input(sf::Event event);
    private:
        sf::Texture title_texture;
        sf::Sprite title_sprite;
    };
    
    #endif
    

    如上所述,该课程将控制游戏菜单。

    实现GameStateManager的过程如下:

    GameStateManager.cpp

    /*
     * GameState Manager will take care of the various states of the game.
     * In particular there will be two states: Menu or Ingame. GameStateManager
     * will load and unload each state as needed.
     *
     * Author: Ben Euden
     * Date: 2/5/2014
     */
    
    #include "GameStateManager.h"
    
    // Class Constructor
    GameStateManager::GameStateManager()
    { 
        game_states = game_states[NUMGAMESTATES];
    
        current_state = MENUSTATE;
        load_state(current_state);
    }
    
    /*
     * Load the current game by creating and initialising the state
     * then storing it in the game_states array.
     * @Param state The state we wish to load.
     */
    void GameStateManager::load_state(int state)
    {
        if(state == MENUSTATE)
            game_states[state] = MenuState(this);
        //if(state == GAMESTATE)
            //game_states[state] = MainGameState(this);    // Not implemented yet.
    }
    
    /*
     * Unload the state we loaded with load_state
     */
    void GameStateManager::unload_state(int state)
    {
        game_states[state] = NULL;
    }
    
    void GameStateManager::set_state(int state)
    {
        unload_state(state);
        current_state = state;
        load_state(state);
    }
    
    void GameStateManager::update()
    {
        try{
            game_states[current_state].update();
        }
        catch(int e)
        {
            std::cout << "Exception occured during update of game state" << e << std::endl;
        }
    }
    
    void GameStateManager::draw(sf::RenderWindow &win)
    {
        try{
            game_states[current_state].draw(&win);
        }
        catch(int e)
        {
            std::cout << "Exception occured when trying to draw gamestate: " << current_state << "Exception number: " << e << std::endl;
        }
    }
    
    void GameStateManager::input(sf::Event event)
    {
        game_states[current_state].input(event);
    }
    

    和MenuState如下:

    /*
     * This class extends the Game State header and will deal with the menu of the game
     * this includes drawing the correct text to the screen, moving the selector and
     * either exiting, bringing up about or starting the game.
     *
     * Author: Ben Euden
     * Date: 2/5/2014
     */
    
    #include "MenuState.h"
    
    
    MenuState::MenuState(GameStateManager gsm)
    {
        gsm = gsm;
        init();
    
    }
    
    void MenuState::init()
    {
        title_texture = sf::Texture();
        title_texture.loadFromFile("sprites/Title.png");
        title_sprite = sf::Sprite();
        title_sprite.setTexture(title_texture);
        title_sprite.setPosition(512, 200);
    }
    
    void MenuState::update(){}
    
    void MenuState::draw(sf::RenderWindow &win)
    {
        win.draw(title_sprite);
    }
    
    void MenuState::input(sf::Event event)
    {
    
    }
    

    请忽略已填充的方法和定位。此时,我开始尝试在出现错误时编译项目(我正在使用Visual Studio)。

    现在明白MainGameState尚未实现,但即使使用MenuState,我确信我在这里缺少一些重要的东西,因为我还在学习C ++。考虑到这一点也请原谅任何破坏公约等我正在学习,所以随时纠正我,我现在学习正确的方法,而不是养成坏习惯。

    总结中,我想了解为什么会收到以下错误:

    protected:
        GameStateManager gsm;
    

    这会产生错误:缺少';'在gsm之前。

    GameState game_states[];
    

    产生以下错误:零大小数组,不允许使用抽象类数组。

    我相信如果我解决这些问题,剩下的就会自行解决。

    感谢您的耐心,时间和帮助。

    Euden

2 个答案:

答案 0 :(得分:2)

简而言之:你不了解C ++的任何基础知识,作为一个初学者,你应该把它当作一种完全不同于Java或C语言的语言,所以你应该立即停止你的项目并找到一本好书对于C ++初学者。不要试图混合你的Java知识,只是填补空白来达到C ++知识,它不会起作用,因为即使语法很接近,它们也是截然不同的野兽。

我总是建议学习C ++作为一种新的和不同的语言,无论你的背景如何。现在你正在做大错误,表明你在学习C ++的错误道路上。你应该回到基础教程(我不是要苛刻,你甚至需要在编译这段代码之前学习基础知识)。

您使用数组和成员,如果它们是引用,则表明您缺乏对“值语义”的理解以及其他一些必须知道C ++用法的基本概念。

例如,如果我有

class A
{
   int k = 42;  // C++11
};

这里A对象将包含一个k对象。我的意思是k不是指向int的指针,它是实际值,在分配给A对象的内存中

所以,如果我有

A my_object; // object on the stack

然后my_object是一个取大小的对象。所以,如果我这样做:

class B
{
   int u;
   A a;
};

然后,B的实例实际上是A对象的大小,加上int的大小。 B对象将在单个内存块中包含所有这些数据。

所以当你这样做时:

class GameState
{
    protected:
    GameStateManager gsm;

你在这里实际做的是在任何GameState对象中构建一个完整的GameStateManager是的,gsm不是参考,它是完整的对象。 你应该在这里做的是使用c ++引用(如果游戏管理器永远不应该改变)或使用指针(或者如果涉及所有权则使用智能poitner)。

我看到很多其他问题,比如你的数组成员进入GameStateManager绝对没有与Java相同的意思。基本上,你在这里注意用C ++编写代码。 (你应该使用std :: vector或std :: array,但是你的GameState是动态的,所以它会是向量或指针数组 - 甚至是map或其他容器。)

由于有太多不足之处,我应该谈到核心问题:

无论你以前学过什么语言,即使是相关的C或Java,从来没有假设你对C ++有任何了解,你绝对不会。你需要作为一个初学者来接近它。将其理解为一种非常新的语言。

并确保您阅读的实际材料为the list provided there。不幸的是,在线学习关于C ++的不良练习非常容易(但它会变得更好)。

另外,您可能需要阅读此内容:https://softwareengineering.stackexchange.com/questions/76675/how-can-a-java-programmer-make-the-most-of-a-new-project-in-c-or-c/76695#76695

顺便提一下,一个相关的建议:阅读"SFML Game Development"本书,例如更简单,更安全(和C ++ - 惯用)的方法来做你想在这里实现的目标。

另一方建议是你的类型名称为avoid using "manager",只会让事情难以理解和设计。

答案 1 :(得分:0)

“零大小数组”错误是由GameState game_states[];引起的。 在C ++中,您必须在声明时指定数组大小,方法是通过专门编写大小或直接初始化它。

示例:

GameState game_states[ ]; // Error, compiler can't know how much memory to reserve for this.
GameState game_states[4]; // OK, explicit size given, compiler will reserve enough memory for 4 `GameState` objects.
GameState game_states[ ] = { GameState( ), GameState( ) }; // OK, direct initialization, compiler will reserve enough memory for 2 `GameState` object.

在你的情况下应该是:

GameState game_states[ NUMGAMESTATES ];

你应该从GameStateManager构造函数中删除以下行:

game_states = game_states[NUMGAMESTATES]; // Meaningless in C++.

“不允许抽象类的数组”也来自此声明,问题是C ++与Java不同。在C ++中,这声明了一个GameState实例的变量,这是不允许的,因为GameState具有纯虚方法,因此无法实例化(正如Java抽象类不能{{1} 1}} ed)。要在C ++中实现这种多态行为,您必须使用指针或引用,这是Java为您隐含的内容。

解决这个问题应该会给你:

new

“失踪”;'在gsm“发生之前,因为编译器无法编译GameState * game_states[ NUMGAMESTATES ]; ,修复我提到的错误应该解决这个问题。

几点提示:

  • 将C ++中的变量视为来自Java的GameStateManager,即使对于您自己声明的类型也是如此。这意味着只需通过声明(不需要int)来实例化它们,并在分配给另一个变量时复制它们。 (默认情况下没有引用语义为Java)
  • 寻找好的C ++书籍/教程,因为您似乎不了解C ++中一些非常重要的基本概念。