尝试将字符串推送到列表后面时出现分段错误

时间:2010-04-02 22:28:24

标签: c++

我正在尝试为我的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;
}

6 个答案:

答案 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));
}