我正在尝试为我的C ++计算器编写一个记录器类,但是在尝试将字符串推入列表时我遇到了问题。
我已经尝试过研究这个问题,并且已经找到了一些相关的信息,但似乎没有任何帮助我的问题。我使用的是一个相当基本的C ++编译器,只有很少的调试实用程序,而且我很长一段时间没有使用过C ++(即便只是少量)。
我的代码:
#ifndef _LOGGER_H_
#define _LOGGER_H_
#include <iostream>
#include <list>
#include <string>
using std::cout;
using std::cin;
using std::endl;
using std::list;
using std::string;
class Logger
{
private:
list<string> mEntries;
public:
Logger() {}
~Logger() {}
// Public Methods
void WriteEntry(const string& entry)
{
mEntries.push_back(entry);
}
void DisplayEntries()
{
cout << endl << "**********************" << endl
<< "* Logger Entries *" << endl
<< "**********************" << endl
<< endl;
for(list<string>::iterator it = mEntries.begin();
it != mEntries.end(); it++)
{
// *** BELOW LINE IS MARKED WITH THE ERROR ***
cout << *it << endl;
}
}
};
#endif
我只是传入一个字符串来调用WriteEntry方法,如下所示:
mLogger->WriteEntry("Testing");
对此有任何建议将不胜感激。
*上面的代码现在已经改变*
现在,行:
cout << *it << endl;
导致相同的错误。我假设这与我如何从迭代器中获取字符串值有关。
我用来调用它的代码在我的main.cpp文件中:
#include <iostream>
#include <string>
#include <sstream>
#include "CommandParser.h"
#include "CommandManager.h"
#include "Exceptions.h"
#include "Logger.h"
using std::string;
using std::stringstream;
using std::cout;
using std::cin;
using std::endl;
#define MSG_QUIT 2384321
#define SHOW_LOGGER true
void RegisterCommands(void);
void UnregisterCommands(void);
int ApplicationLoop(void);
void CheckForLoggingOutput(void);
void ShowDebugLog(void);
// Operations
double Operation_Add(double* params);
double Operation_Subtract(double* params);
double Operation_Multiply(double* params);
double Operation_Divide(double* params);
// Variable
CommandManager *mCommandManager;
CommandParser *mCommandParser;
Logger *mLogger;
int main(int argc, const char **argv)
{
mLogger->WriteEntry("Registering commands...\0");
// Make sure we register all commands first
RegisterCommands();
mLogger->WriteEntry("Command registration complete.\0");
// Check the input to see if we're using the program standalone,
// or not
if(argc == 0)
{
mLogger->WriteEntry("Starting application message pump...\0");
// Full version
int result;
do
{
result = ApplicationLoop();
} while(result != MSG_QUIT);
}
else
{
mLogger->WriteEntry("Starting standalone application...\0");
// Standalone - single use
// Join the args into a string
stringstream joinedStrings(argv[0]);
for(int i = 1; i < argc; i++)
{
joinedStrings << argv[i];
}
mLogger->WriteEntry("Parsing argument '" + joinedStrings.str() + "'...\0");
// Parse the string
mCommandParser->Parse(joinedStrings.str());
// Get the command names from the parser
list<string> commandNames = mCommandParser->GetCommandNames();
// Check that all of the commands have been registered
for(list<string>::iterator it = commandNames.begin();
it != commandNames.end(); it++)
{
mLogger->WriteEntry("Checking command '" + *it + "' is registered...\0");
if(!mCommandManager->IsCommandRegistered(*it))
{
// TODO: Throw exception
mLogger->WriteEntry("Command '" + *it + "' has not been registered.\0");
}
}
// Get each command from the parser and use it's values
// to invoke the relevant command from the manager
double results[commandNames.size()];
int currentResultIndex = 0;
for(list<string>::iterator name_iterator = commandNames.begin();
name_iterator != commandNames.end(); name_iterator++)
{
string paramString = mCommandParser->GetCommandValue(*name_iterator);
list<string> paramStringArray = StringHelper::Split(paramString, ' ');
double params[paramStringArray.size()];
int index = 0;
for(list<string>::iterator param_iterator = paramStringArray.begin();
param_iterator != paramStringArray.end(); param_iterator++)
{
// Parse the current string to a double value
params[index++] = atof(param_iterator->c_str());
}
mLogger->WriteEntry("Invoking command '" + *name_iterator + "'...\0");
results[currentResultIndex++] =
mCommandManager->InvokeCommand(*name_iterator, params);
}
// Output all results
for(int i = 0; i < commandNames.size(); i++)
{
cout << "Result[" << i << "]: " << results[i] << endl;
}
}
mLogger->WriteEntry("Unregistering commands...\0");
// Make sure we clear up our resources
UnregisterCommands();
mLogger->WriteEntry("Command unregistration complete.\0");
if(SHOW_LOGGER)
{
CheckForLoggingOutput();
}
system("PAUSE");
return 0;
}
void RegisterCommands()
{
mCommandManager = new CommandManager();
mCommandParser = new CommandParser();
mLogger = new Logger();
// Known commands
mCommandManager->RegisterCommand("add", &Operation_Add);
mCommandManager->RegisterCommand("sub", &Operation_Subtract);
mCommandManager->RegisterCommand("mul", &Operation_Multiply);
mCommandManager->RegisterCommand("div", &Operation_Divide);
}
void UnregisterCommands()
{
// Unregister each command
mCommandManager->UnregisterCommand("add");
mCommandManager->UnregisterCommand("sub");
mCommandManager->UnregisterCommand("mul");
mCommandManager->UnregisterCommand("div");
// Delete the logger pointer
delete mLogger;
// Delete the command manager pointer
delete mCommandManager;
// Delete the command parser pointer
delete mCommandParser;
}
int ApplicationLoop()
{
return MSG_QUIT;
}
void CheckForLoggingOutput()
{
char answer = 'n';
cout << endl << "Do you wish to view the debug log? [y/n]: ";
cin >> answer;
switch(answer)
{
case 'y':
ShowDebugLog();
break;
}
}
void ShowDebugLog()
{
mLogger->DisplayEntries();
}
// Operation Definitions
double Operation_Add(double* values)
{
double accumulator = 0.0;
// Iterate over all values and accumulate them
for(int i = 0; i < (sizeof values) - 1; i++)
{
accumulator += values[i];
}
// Return the result of the calculation
return accumulator;
}
double Operation_Subtract(double* values)
{
double accumulator = 0.0;
// Iterate over all values and negativel accumulate them
for(int i = 0; i < (sizeof values) - 1; i++)
{
accumulator -= values[i];
}
// Return the result of the calculation
return accumulator;
}
double Operation_Multiply(double* values)
{
double accumulator = 0.0;
for(int i = 0; i < (sizeof values) - 1; i++)
{
accumulator *= values[i];
}
// Return the value of the calculation
return accumulator;
}
double Operation_Divide(double* values)
{
double accumulator = 0.0;
for(int i = 0; i < (sizeof values) - 1; i++)
{
accumulator /= values[i];
}
// Return the result of the calculation
return accumulator;
}
答案 0 :(得分:4)
您记得在某个时候致电mLogger = new Logger
吗?在写信之前你有没有得到delete mLogger
?
尝试在valgrind中运行程序,看看是否发现任何内存错误。
答案 1 :(得分:3)
编辑完成后,解决方案似乎很清楚:
你在main()的第一行是:
mLogger->WriteEntry("Registering commands...\0");
这里mLogger是一个从未被初始化的指针。这是“未定义的行为”,这意味着任何东西都可以附加,通常是坏事。
要解决此问题,您可以将其设为“普通”变量,而不是指针或使用new
创建Logger实例(在声明处或作为main中的第一行)。
我建议您不要使用指针来确保记录器始终存在并自动销毁。
顺便说一句,您似乎想要使用指针在堆上创建对象的每个实例。如果没有必要,不建议使用。如果要明确说明实例对象的创建(使用new
)和销毁(使用delete
),则应仅使用指针。如果您只是在特定范围内需要它,请不要使用指针。您可能来自Java或C#等其他语言,其中引用了所有对象。如果是这样,你应该开始像不同的语言一样学习C ++以避免这种问题。您应该了解RAII和其他C ++ scpecific范例,这些范例是您无法用这些语言学习的。如果你来自C,你也应该把它当作另一种语言。这可能会帮助您避免复杂的问题,例如您在此处展示的问题。我建议你在stackoverflow上阅读一些C ++指针,引用和RAII相关问题。
首先,您不需要在堆上创建std :: list。您应该将它用作该类的普通成员。
class Logger
{
private:
list<string> mEntries; // no need to use a pointer
public:
Logger() // initialization is automatic, no need to do anything
{
}
~Logger() // clearing and destruction is automatic too, no need to do anything
{
}
//...
};
接下来,此代码中不存在entryData
,因此我猜您要使用entry
。如果它不是拼写错误,那么你就不会提供entryData的定义,这肯定是你问题的根源。
事实上,我会以这样的方式编写你的课程:
class Logger
{
private:
list<string> mEntries;
public:
// no need for constructor and destructor, use the default ones
// Public Methods
void WriteEntry(const string& entry) // use a const reference to avoid unnecessary copy (even with optimization like NRVO)
{
mEntries.push_back( entry ); // here the list will create a node with a string inside, so this is exactly like calling the copy constructor
}
void DisplayEntries()
{
cout << endl << "**********************" << endl
<< "* Logger Entries *" << endl
<< "**********************" << endl
<< endl;
for(list<string>::iterator it = mEntries.begin();
it != mEntries.end(); ++it) // if you want to avoid unnecessary copies, use ++it instead of it++
{
cout << *it << endl;
}
}
};
可以肯定的是,您的段错误来自本课程以外的用法。
答案 2 :(得分:2)
Logger的实例是否被复制到任何地方(通过复制构造函数或operator =)?由于您有mEntries
作为指向列表的指针,因此如果您复制Logger实例,它们将共享指针的值,当一个指针被销毁时,它将删除该列表。然后原件有一个悬空指针。快速检查是使复制构造函数和operator = private而不是实现:
private:
void operator=(const Logger &); // not implemented
Logger(const Logger &); // not implemented
重新编译时,编译器将标记任何Logger实例的任何副本。
如果您需要复制Logger实例,则修复方法是遵循规则3:
http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29
您可以通过消除对析构函数的需要(通过不使用指针:list<string> mEntries
),或者将所需的代码添加到复制构造函数和operator =来制作列表的深层副本来实现此目的。
答案 3 :(得分:1)
你只需要做
list<string> entries;
entries.push_back();
您无需创建指向条目的指针。
答案 4 :(得分:0)
虽然你打字
,但没什么太明显的mEntries->push_back(string(entryData));
我想你的意思是entry
而不是entryData
。您也不需要在该行上进行string
转换,并且您的函数应该通过const引用获取entry
。
但是,这些都不会导致您的程序出现段错误。你使用什么编译器?
答案 5 :(得分:0)
你错过了复制构造函数。如果复制了Logger对象并删除了原始对象,则您将取消引用先前删除的内存。
问题的简化示例
Logger a;
{
Logger b;
a=b;
}
a.WriteEntry("Testing");
添加复制构造函数。
Logger(const Logger& item)
{
mEntries = new list<string>();
std::copy(item.mEntries->begin(), item.mEntries->end(), std::back_inserter(*mEntries));
}