2014-02-06 30 views
0

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

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

  1. 菜单状态:游戏的菜单被绘制,游戏将从这里开始。

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

为了做到这一点,我已经创建了以下类:

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 

现在你可能已经注意到了Array游戏状态管理器中的GameStates?这提供了一个我不明白的错误:零大小的数组。这是否意味着初始化需要在头文件中进行?进一步说到这一点,编译器提到了一个不允许的抽象类数组?

第二个问题是抽象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.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) 
{ 

} 

请忽略inplemented方法和定位运动用作如下

实施GameStateManager完成。此时我开始尝试编译该项目(我使用Visual Studio)出现错误时。

现在明白,MainGameState尚未实现,但即使使用MenuState我确信我错过了一些至关重要的事情,因为我仍在学习C++。考虑到这一点,请原谅任何破坏公约等,我正在学习,所以随时纠正我,我更好地学习正确的方式,而不是养成坏习惯。

总结,我想知道为什么我收到以下错误:

protected: 
    GameStateManager gsm; 

这将产生错误:缺少“;”在gsm之前。

GameState game_states[]; 

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

我相信如果我解决了这些问题,其余的人都会自行解决。

谢谢你的耐心,时间和对此的帮助。

Euden

+0

那么,你有一对圆形包括,所以这不会帮助... –

+0

如果你可以足够的指出这些我会很感激它(这可能是一个错误,由于编码延迟到晚上-_-) –

回答

2

要短:你不知道C++中的任何基础知识,以及作为一个初学者,你真的应该的形式给出了它作为一个完全不同的语言比Java或C,所以你应该马上停止项目并找到一本适合C++初学者的好书。不要试图混合你的Java知识,只是填补空白,以达到C++知识,它不会工作,因为即使语法接近,它们是广泛不同的野兽。

我总是推荐学习C++作为一种新的和不同的语言,无论你的背景如何。现在你正在犯大错误,显示你在学习C++的错误途径。你应该回到基本的教程(我不想苛刻,你甚至在管理编译此代码之前需要学习基础知识)。

使用数组和成员就好像他们是引用一样,表明您对“价值语义”以及几个其他必须知道的C++用法的基本概念缺乏理解。

例如,如果我有

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

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

所以,如果我有

A my_object; // object on the stack 

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

class B 
{ 
    int u; 
    A a; 
}; 

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

所以,当你这样做:

class GameState 
{ 
    protected: 
    GameStateManager gsm; 

你真正在这里做的是,你建立一个完整的GameStateManager到任何游戏状态对象。 是的,gsm不是一个参考,它是完整的对象。 你应该在这里做的是使用C++参考(如果游戏管理员不应该改变)或者使用指针(或者如果涉及所有权的话,使用一个智能poitner)。

我看到很多其他问题,比如你的数组成员进入GameStateManager的含义与Java中的绝对不一样。基本上,你注意到在C++中编码。 (并且你应该使用std :: vector或者std :: array,但是你的GameState是动态的,所以它可以是向量或者指针数组 - 甚至是映射或者其他容器)。

由于有太多的时候,我应该得到的核心点:

无论这些都与你以前学过的语言,甚至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"在你的类型名称,它只会让事情很难理解和设计。

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抽象类不能实例化一样)。为了在C++中实现这种多态行为,你必须使用指针或引用,这是Java为你使用的内容。

修复这应该给你:

GameState * game_states[ NUMGAMESTATES ]; 

“缺少 ';'因为编译器无法编译GameStateManager,因此修复我之前提到的错误应该可以解决这个问题。

一些提示:

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