我的一位朋友和其他人写了下面的代码,根据我的C ++知识应该是非常危险的:
Recovery& Recovery::LoadRecoverFile() {
fstream File("files/recover.dat", ios::in | ios::binary);
if (File.is_open()) {
while (!File.eof()) {
File.read(reinterpret_cast<char*>(this), sizeof(Recovery)); // <----- ?
}
}
File.close();
return *this; // <----- ?
}
你能否告诉我为什么这很糟糕,应该如何正确地完成?
他们基本上将类Recovery的对象写入文件,并在需要时使用上述方法读取它。
编辑: 只是提供一些有关代码的其他信息。这就是恢复类所包含的内容。
class Recovery {
public:
Recovery();
virtual ~Recovery();
void createRecoverFile();
void saveRecoverFile( int level, int win, int credit, gameStates state, int clicks );
Recovery& LoadRecoverFile();
const vector<Card>& getRecoverCards() const;
void setRecoverCards(const vector<Card>& recoverCards);
int getRecoverClicks() const;
void setRecoverClicks(int recoverClicks);
int getRecoverCredit() const;
void setRecoverCredit(int recoverCredit);
int getRecoverLevel() const;
void setRecoverLevel(int recoverLevel);
gameStates getRecoverState() const;
void setRecoverState(gameStates recoverState);
int getRecoverTime() const;
void setRecoverTime(int recoverTime);
int getRecoverWin() const;
void setRecoverWin(int recoverWin);
private:
int m_RecoverLevel;
int m_RecoverCredit;
gameStates m_RecoverState;
};
这会将对象保存到文件中:
void Recovery::saveRecoverFile(int level, int win, int credit, gameStates state,
int clicks) {
m_RecoverLevel = level;
m_RecoverCredit = credit;
m_RecoverState = state;
ofstream newFile("files/recover.dat", ios::binary | ios::out);
if (newFile.is_open()) {
newFile.write(reinterpret_cast<char*>(this), sizeof(Recovery));
}
newFile.close();
}
它是如何使用的:
m_Recovery.LoadRecoverFile();
credit.IntToTextMessage(m_Recovery.getRecoverCredit());
level.IntToTextMessage(m_Recovery.getRecoverLevel());
m_cardLogic.setTempLevel(m_Recovery.getRecoverLevel());
Timer::g_Timer->StartTimer(m_Recovery.getRecoverLevel() + 3);
答案 0 :(得分:2)
可能是undefined behavior(除非Recovery
是仅由标量字段构成的POD)。
如果Recovery
类有一个vtable,它可能无法工作,除非正在读取的进程可能与编写它的进程相同。 Vtables包含函数指针(通常是某些机器代码的地址)。并且这些函数指针将从一个进程变化到另一个进程(即使它们运行相同的二进制文件),例如,因为ASLR。
如果Recovery
包含其他对象(例如std::vector<std::shared_ptr<Recovery>>
....或您的gameStates
),它也无法工作,因为这些子对象赢了&#39 ;正确构造。
它有时可以工作。但您显然正在寻找的是serialization(然后我会建议使用JSON等文字格式,但另请参阅libs11n)或application checkpointing。您应该从一开始就为这些目标设计应用程序。
答案 1 :(得分:2)
这实际上取决于Recovery
对象包含的内容。如果它包含指向数据的指针,那么打开资源描述符以及类似的东西,你将无法以有意义的方式将它们存储在文件中。以这种方式恢复指针可能会设置其值,但它指向的值肯定不会是您预期的值。
如果Recovery
是POD,这应该有效。
您可能需要查看与您类似的this question和this other question。
正如Galik正确指出的那样,使用
while (!File.eof()) {
没有多大意义。相反,你应该使用
if ( File.read(/* etc etc */) ) {
// Object restored successfully.
}
else {
// Revert changes and signal that object was not loaded.
}
函数的调用者需要知道加载是否成功。该方法已经是一个成员函数,因此更好的定义可能是:
/* Returns true if the file was read successfully, false otherwise.
* If reading fails the previous state of the object is not modified.
*/
bool Recovery::LoadRecoverFile(const std::string & filename);
答案 2 :(得分:1)
我个人建议以文本格式而不是二进制格式存储游戏状态。像这样的二进制数据是不可移植的,有时甚至在同一台计算机上的同一编译器的不同版本之间,甚至使用不同的编译器配置选项。
如果您要使用二进制路由(或不是),我会看到代码中出现的主要问题是缺少错误检查。让Recovery
对象通过自己的petard提升自身的整个想法使错误检查变得非常困难。
我已经敲了一些我觉得更健壮的东西。我不知道您正在使用的正确程序结构,因此这可能无法满足您的需求。但它可以作为如何处理这个问题的一个例子。
最重要的是始终 检查错误,在适当的时候报告并将它们返回给来电者。< / p>
enum gameStates
{
MENU, STARTGAME, GAMEOVER, RECOVERY, RULES_OF_GAMES, VIEW_CARDS, STATISTIC
};
const std::string RECOVER_FILE = "files/recover.dat";
struct Recovery
{
int m_RecoverLevel;
int m_RecoverCredit;
gameStates m_RecoverState;
};
struct WhateverClass
{
Recovery m_Recovery;
bool LoadRecoverFile(Recovery& rec);
public:
bool recover();
};
// Supply the Recover object to be restored and
// return true or false to know it succeeded or not
bool WhateverClass::LoadRecoverFile(Recovery& rec)
{
std::ifstream file(RECOVER_FILE, std::ios::binary);
if(!file.is_open())
{
log("ERROR: opening the recovery file: " << RECOVER_FILE);
return false;
}
if(!file.read(reinterpret_cast<char*>(&rec), sizeof(Recovery)))
{
log("ERROR: reading from recovery file: " << RECOVER_FILE);
return false;
}
return true;
}
bool WhateverClass::recover()
{
if(!LoadRecoverFile(m_Recovery))
return false;
credit.IntToTextMessage(m_Recovery.getRecoverCredit());
level.IntToTextMessage(m_Recovery.getRecoverLevel());
m_cardLogic.setTempLevel(m_Recovery.getRecoverLevel());
Timer::g_Timer->StartTimer(m_Recovery.getRecoverLevel() + 3);
return true;
}
希望这有帮助。
答案 3 :(得分:-1)
大家好吧其实类StateManager内容整数:
#ifndef STATEMANAGER_H_
#define STATEMANAGER_H_
enum gameStates {
MENU, STARTGAME, GAMEOVER, RECOVERY, RULES_OF_GAMES, VIEW_CARDS, STATISTIC
};
class StateManager {
public:
static StateManager* stateMachine;
StateManager();
virtual ~StateManager();
gameStates getCurrentGameStates() const;
void setCurrentGameStates(gameStates currentGameStates);
private:
gameStates m_currentGameStates;
};
#endif /* STATEMANAGER_H_ */