从Java到C ++我试图通过面向对象来理解抽象。
将这个放到一个实际的例子中,我正在开发一个使用SFML库进行图形处理的小游戏。然而,这个问题与此无关,只需将其视为背景信息。无论如何,游戏的工作方式是处理许多不同的状态。在这种情况下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
现在您可能已经注意到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
答案 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 ++的不良练习非常容易(但它会变得更好)。
顺便提一下,一个相关的建议:阅读"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 ];
,修复我提到的错误应该解决这个问题。
几点提示:
GameStateManager
,即使对于您自己声明的类型也是如此。这意味着只需通过声明(不需要int
)来实例化它们,并在分配给另一个变量时复制它们。 (默认情况下没有引用语义为Java)