2010-04-02 83 views
1

我正在嘗試爲我的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

'entryData'是'entry'的拼寫錯誤嗎? – Klaim 2010-04-02 22:32:11

+1

你爲什麼在這裏動態分配?我認爲沒有理由。始終更喜歡自動(堆棧)分配。 – GManNickG 2010-04-02 22:39:04

+0

請在編輯時使用「代碼」按鈕,以確保您的代碼正確顯示。 – Klaim 2010-04-02 23:13:21

回答

4

您是否記得在某個時候撥打mLogger = new Logger?寫信之前,你是否喜歡delete mLogger

嘗試在valgrind中運行您的程序以查看它是否發現任何內存錯誤。

+0

你是對的,看着第一行int他編輯的源代碼,你可以看到沒有被初始化的記錄器指針。 – Klaim 2010-04-02 23:19:04

+0

謝謝你向我指出這一點。我錯過了一個傻瓜!我想我只是看起來太接近標記的錯誤而不是圍繞它的使用。謝謝。 – 2010-04-03 00:44:28

+1

當然,這裏真正的愚蠢的東西首先使用動態分配... – rlbond 2010-04-03 04:51:21

1

你只需要做

list<string> entries; 

entries.push_back(); 

你並不需要創建一個指針參賽作品。

0

沒有什麼太明顯了,雖然你鍵入

mEntries->push_back(string(entryData)); 

我htink你的意思是entry而不是entryData。您也不需要該行上的string轉換,並且您的函數應該通過const引用獲取entry

但是,這些東西都不會導致您的程序段錯誤。你使用什麼編譯器?

+0

對不起,我的錯。複製和粘貼錯字^^是的,它應該是: mEntries-> push_back(entry); – 2010-04-02 22:37:55

3

您的編輯後,該解決方案似乎是清楚的:

你在主第一行()是:

mLogger->WriteEntry("Registering commands...\0"); 

這裏mLogger是從未初始化的指針。這是「未定義的行爲」,意思是任何事情都可能發生,通常是不好的事情。

爲了解決這個問題,您可以使用new(無論是在聲明中,還是作爲main中的第一行)使其成爲「普通」變量,而不是指針或創建記錄器實例。

我建議你不要使用指針來確保記錄器總是存在並自動銷燬。

順便說一下,它似乎是你想要使用指針在堆上創建對象的每個實例。如果沒有必要,不推薦。如果您想明確聲明實例對象的創建(使用new)和銷燬(使用delete),則只應使用指針。如果你只是需要它在一個特定的範圍內,不要使用指針。您可能來自其他語言,如Java或C#,其中引用了所有對象。如果是這樣,你應該開始學習C++,像一種不同的語言,以避免這種問題。您應該瞭解您無法在這些語言中學習的RAII和其他C++特定範例。如果你來自C語言,你也應該把它作爲一種不同的語言。這可以幫助您避免像您在這裏展示的那樣複雜的問題。我可以建議你閱讀一些C++指針,參考和有關stackoverflow的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; 
    } 
} 
}; 

什麼肯定的是,你的段錯誤是使用這個類之外。

+1

它顯然是編輯代碼:他是segfaulting,*不應該是編譯器錯誤。 – 2010-04-02 22:37:43

+0

是的,但它也可能是一個全局變量。但我更喜歡編修錯字。 – Klaim 2010-04-02 22:41:25

+0

好吧,這可能現在是相當多的了,但我已經改變了我的代碼,不使用mEntries作爲指針,現在我在輸出行上得到相同的錯誤:{* it << endl; 我假設這是與我的調用到迭代器來拉出價值。 – 2010-04-02 22:42:30

0

您錯過了複製構造函數。如果記錄器對象被複制並且原始被刪除,您將取消引用先前刪除的內存。

問題

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)); 
} 
2

Logger被一個實例拷貝到任何地方(通過拷貝構造函數或運算符=)?由於您有mEntries作爲指向列表的指針,因此如果您複製記錄器的實例,它們將共享指針的值,並在其中一個被破壞時刪除該列表。原來有一個懸掛指針。快速檢查是使拷貝構造函數和operator =私人和未實現:

private: 
    void operator=(const Logger &); // not implemented 
    Logger(const Logger &); // not implemented 

當您重新編譯,編譯器就會記錄儀的任何實例的任何副本。

如果你需要複製記錄儀的情況下,解決辦法是遵循3條: http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29 您可以通過消除對析構函數的需求(不使用指針:list<string> mEntries)做到這一點,或通過添加複製構造函數和操作符所需的代碼=進行列表的深層複製。

+0

太慢:)但我喜歡鏈接和提及operator = so upvoted。 – 2010-04-02 22:56:15

相關問題