您好(并提前致谢)
我有点夸张,我似乎无法弄清楚为什么我会发生错误。
几点说明:
这是代码
main.cpp:
// ///////////////////////////////////////////////////////////////////////////////////
// INCLUDES (C/C++ Std Library)
#include <cstdlib> /// EXIT_SUCCESS, EXIT_FAILURE
#include <iostream> /// cin, cout, ifstream
#include <cassert> /// assert
// ///////////////////////////////////////////////////////////////////////////////////
// DEPENDENCIES (custom header files)
#include "dict.h" /// Header for the dictionary class
// ///////////////////////////////////////////////////////////////////////////////////
// PRE-PROCESSOR CONSTANTS
#define ENTER '\n' /// Used to accept new lines, quit program.
#define SPACE ' ' /// One way to end the program
// ///////////////////////////////////////////////////////////////////////////////////
// CUSTOM DATA TYPES
/// File Namespace -- keep it local
namespace
{
/// Possible program prompts to display for the user.
enum FNS_Prompts
{
fileName_, /// prints out the name of the file
noFile_, /// no file was passed to the program
tooMany_, /// more than one file was passed to the program
noMemory_, /// Not enough memory to use the program
usage_, /// how to use the program
word_, /// ask the user to define a word.
notFound_, /// the word is not in the dictionary
done_, /// the program is closing normally
};
}
// ///////////////////////////////////////////////////////////////////////////////////
// Namespace
using namespace std; /// Nothing special in the way of namespaces
// ///////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/** prompt() prompts the user to do something, uses enum Prompts for parameter.
*/
void prompt(FNS_Prompts msg /** determines the prompt to use*/)
{
switch(msg)
{
case fileName_ :
{
cout << ENTER << ENTER << "The file name is: ";
break;
}
case noFile_ :
{
cout << ENTER << ENTER << "...Sorry, a dictionary file is needed. Try again." << endl;
break;
}
case tooMany_ :
{
cout << ENTER << ENTER << "...Sorry, you can only specify one dictionary file. Try again." << endl;
break;
}
case noMemory_ :
{
cout << ENTER << ENTER << "...Sorry, there isn't enough memory available to run this program." << endl;
break;
}
case usage_ :
{
cout << "USAGE:" << endl
<< " lookup.exe [dictionary file name]" << endl << endl;
break;
}
case done_ :
{
cout << ENTER << ENTER << "like Master P says, \"Word.\"" << ENTER << endl;
break;
}
case word_ :
{
cout << ENTER << ENTER << "Enter a word in the dictionary to get it's definition." << ENTER
<< "Enter \"?\" to get a sorted list of all words in the dictionary." << ENTER
<< "... Press the Enter key to quit the program: ";
break;
}
case notFound_ :
{
cout << ENTER << ENTER << "...Sorry, that word is not in the dictionary." << endl;
break;
}
default :
{
cout << ENTER << ENTER << "something passed an invalid enum to prompt(). " << endl;
assert(false); /// something passed in an invalid enum
}
}
}
/** useDictionary() uses the dictionary created by createDictionary
* - prompts user to lookup a word
* - ends when the user enters an empty word
*/
void useDictionary(Dictionary &d)
{
char *userEntry = new char; /// user's input on the command line
if( !userEntry ) // check the pointer to the heap
{ cout << ENTER << MEM_ERR_MSG << endl; exit(EXIT_FAILURE);
}
do
{
prompt(word_);
// test code
cout << endl << "----------------------------------------" << endl
<< "Enter something: ";
cin.getline(userEntry, INPUT_LINE_MAX_LEN, ENTER);
cout << ENTER << userEntry << endl;
}while ( userEntry[0] != NIL && userEntry[0] != SPACE );
// GARBAGE COLLECTION
delete[] userEntry;
}
/** Program Entry
* Reads in the required, single file from the command prompt.
* - If there is no file, state such and error out.
* - If there is more than one file, state such and error out.
* - If there is a single file:
* - Create the database object
* - Populate the database object
* - Prompt the user for entry
* main() will return EXIT_SUCCESS upon termination.
*/
int main(int argc, /// the number of files being passed into the program
char *argv[] /// pointer to the filename being passed into tthe program
)
{
// EXECUTE
/* Testing code * /
char tempFile[INPUT_LINE_MAX_LEN] = {NIL};
cout << "enter filename: ";
cin.getline(tempFile, INPUT_LINE_MAX_LEN, '\n');
*/
// uncomment after successful debugging
if(argc <= 1)
{
prompt(noFile_); prompt(usage_);
return EXIT_FAILURE; /// no file was passed to the program
}
else if(argc > 2)
{
prompt(tooMany_); prompt(usage_);
return EXIT_FAILURE; /// more than one file was passed to the program
}
else
{
prompt(fileName_); cout << argv[1]; // print out name of dictionary file
if( !argv[1] )
{
prompt(noFile_); prompt(usage_);
return EXIT_FAILURE; /// file does not exist
}
/*
file.open( argv[1] ); // open file
numEntries >> in.getline(file); // determine number of dictionary objects to create
file.close(); // close file
Dictionary[ numEntries ](argv[1]); // create the dictionary object
*/
// TEMPORARY FILE FOR TESTING!!!!
//Dictionary scrabble(tempFile);
Dictionary scrabble(argv[1]); // creaate the dicitonary object
//*/
useDictionary(scrabble); // prompt the user, use the dictionary
}
// exit
return EXIT_SUCCESS; /// terminate program.
}
Dict.h /的.cpp
#ifndef DICT_H
#define DICT_H
// ///////////////////////////////////////////////////////////////////////////////////
// DEPENDENCIES (Custom header files)
#include "entry.h" /// class for dictionary entries
// ///////////////////////////////////////////////////////////////////////////////////
// PRE-PROCESSOR MACROS
#define INPUT_LINE_MAX_LEN 256 /// Maximum length of each line in the dictionary file
class Dictionary
{
public :
//
// Do NOT modify the public section of this class
//
typedef void (*WordDefFunc)(const char *word, const char *definition);
Dictionary( const char *filename );
~Dictionary();
const char *lookupDefinition( const char *word );
void forEach( WordDefFunc func );
private :
//
// You get to provide the private members
//
// VARIABLES
int m_numEntries; /// stores the number of entries in the dictionary
Entry *m_DictEntry_ptr; /// points to an array of class Entry
// Private Functions
};
#endif
-----------------------------------
// ///////////////////////////////////////////////////////////////////////////////////
// INCLUDES (C/C++ Std Library)
#include <iostream> /// cout, getline
#include <fstream> // ifstream
#include <cstring> /// strchr
// ///////////////////////////////////////////////////////////////////////////////////
// DEPENDENCIES (custom header files)
#include "dict.h" /// Header file required by assignment
//#include "entry.h" /// Dicitonary Entry Class
// ///////////////////////////////////////////////////////////////////////////////////
// PRE-PROCESSOR MACROS
#define COMMA ',' /// Delimiter for file
#define ENTER '\n' /// Carriage return character
#define FILE_ERR_MSG "The data file could not be opened. Program will now terminate."
#pragma warning(disable : 4996) /// turn off MS compiler warning about strcpy()
// ///////////////////////////////////////////////////////////////////////////////////
// Namespace reference
using namespace std;
// ///////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBER FUNCTIONS
/**
* Sorts the dictionary entries.
*/
/*
static void sortDictionary(?)
{
// sort through the words using qsort
}
*/
/** NO LONGER NEEDED??
* parses out the length of the first cell in a delimited cell
* /
int getWordLength(char *str /// string of data to parse
)
{
return strcspn(str, COMMA);
}
*/
// ///////////////////////////////////////////////////////////////////////////////////
// PUBLIC MEMBER FUNCTIONS
/** constructor for the class
* - opens/reads in file
* - creates initializes the array of member vars
* - creates pointers to entry objects
* - stores pointers to entry objects in member var
* - ? sort now or later?
*/
Dictionary::Dictionary( const char *filename )
{
// Create a filestream, open the file to be read in
ifstream dataFile(filename, ios::in );
/*
if( dataFile.fail() )
{ cout << FILE_ERR_MSG << endl; exit(EXIT_FAILURE);
}
*/
if( dataFile.is_open() )
{
// read first line of data
// TEST CODE in.getline(dataFile, INPUT_LINE_MAX_LEN) >> m_numEntries;
// TEST CODE char temp[INPUT_LINE_MAX_LEN] = {NIL};
// TEST CODE dataFile.getline(temp,INPUT_LINE_MAX_LEN,'\n');
dataFile >> m_numEntries; /** Number of terms in the dictionary file
* \todo find out how many lines in the file, subtract one, ingore first line
*/
//create the array of entries
m_DictEntry_ptr = new Entry[m_numEntries];
// check for valid memory allocation
if( !m_DictEntry_ptr )
{ cout << MEM_ERR_MSG << endl; exit(EXIT_FAILURE);
}
// loop thru each line of the file, parsing words/def's and populating entry objects
for(int EntryIdx = 0; EntryIdx < m_numEntries; ++EntryIdx)
{
// VARIABLES
char *tempW_ptr; /// points to a temporary word
char *tempD_ptr; /// points to a temporary def
char *w_ptr; /// points to the word in the Entry object
char *d_ptr; /// points to the definition in the Entry
int tempWLen; /// length of the temp word string
int tempDLen; /// length of the temp def string
char tempLine[INPUT_LINE_MAX_LEN] = {NIL}; /// stores a single line from the file
// EXECUTE
// getline(dataFile, tempLine) // get a "word,def" line from the file
dataFile.getline(tempLine, INPUT_LINE_MAX_LEN); // get a "word,def" line from the file
// Parse the string
tempW_ptr = tempLine; // point the temp word pointer at the first char in the line
tempD_ptr = strchr(tempLine, COMMA); // point the def pointer at the comma
*tempD_ptr = NIL; // replace the comma with a NIL
++tempD_ptr; // increment the temp def pointer
// find the string lengths... +1 to account for terminator
tempWLen = strlen(tempW_ptr) + 1;
tempDLen = strlen(tempD_ptr) + 1;
// Allocate heap memory for the term and defnition
w_ptr = new char[ tempWLen ];
d_ptr = new char[ tempDLen ];
// check memory allocation
if( !w_ptr && !d_ptr )
{ cout << MEM_ERR_MSG << endl; exit(EXIT_FAILURE);
}
// copy the temp word, def into the newly allocated memory and terminate the strings
strcpy(w_ptr,tempW_ptr); w_ptr[tempWLen] = NIL;
strcpy(d_ptr,tempD_ptr); d_ptr[tempDLen] = NIL;
// set the pointers for the entry objects
m_DictEntry_ptr[ EntryIdx ].setWordPtr(w_ptr);
m_DictEntry_ptr[ EntryIdx ].setDefPtr(d_ptr);
}
// close the file
dataFile.close();
}
else
{ cout << ENTER << FILE_ERR_MSG << endl; exit(EXIT_FAILURE);
}
}
/**
* cleans up dynamic memory
*/
Dictionary::~Dictionary()
{
delete[] m_DictEntry_ptr; /// thou shalt not have memory leaks.
}
/**
* Looks up definition
*/
/*
const char *lookupDefinition( const char *word )
{
// print out the word ---- definition
}
*/
/**
* prints out the entire dictionary in sorted order
*/
/*
void forEach( WordDefFunc func )
{
// to sort before or now.... that is the question
}
*/
Entry.h / CPP
#ifndef ENTRY_H
#define ENTRY_H
// ///////////////////////////////////////////////////////////////////////////////////
// INCLUDES (C++ Std lib)
#include <cstdlib> /// EXIT_SUCCESS, NULL
// ///////////////////////////////////////////////////////////////////////////////////
// PRE-PROCESSOR MACROS
#define NIL '\0' /// C-String terminator
#define MEM_ERR_MSG "Memory allocation has failed. Program will now terminate."
// ///////////////////////////////////////////////////////////////////////////////////
// CLASS DEFINITION
class Entry
{
public:
Entry(void) : m_word_ptr(NULL), m_def_ptr(NULL) { /* default constructor */ };
void setWordPtr(char *w_ptr); /// sets the pointer to the word - only if the pointer is empty
void setDefPtr(char *d_ptr); /// sets the ponter to the definition - only if the pointer is empty
/// returns what is pointed to by the word pointer
char getWord(void) const { return *m_word_ptr; }
/// returns what is pointed to by the definition pointer
char getDef(void) const { return *m_def_ptr; }
private:
char *m_word_ptr; /** points to a dictionary word */
char *m_def_ptr; /** points to a dictionary definition */
};
#endif
--------------------------------------------------
// ///////////////////////////////////////////////////////////////////////////////////
// DEPENDENCIES (custom header files)
#include "entry.h" /// class header file
// ///////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
/*
* only change the word member var if it is in its initial state
*/
void Entry::setWordPtr(char *w_ptr)
{
if(m_word_ptr == NULL)
{ m_word_ptr = w_ptr;
}
}
/*
* only change the def member var if it is in its initial state
*/
void Entry::setDefPtr(char *d_ptr)
{
if(m_def_ptr == NULL)
{ m_word_ptr = d_ptr;
}
}
答案 0 :(得分:2)
这里容易出现的问题是:
cin.getline(userEntry, INPUT_LINE_MAX_LEN, ENTER);
第一个问题是,除非INPUT_LINE_MAX_LEN == 1,否则该行是错误的 这很容易修复声明og userEntry更改为:
userEntry = new char[INPUT_LINE_MAX_LEN + 1];
我看到的第二个问题是:
// GARBAGE COLLECTION
delete[] userEntry;
这是错误的,因为您只为userEntry分配一个char而不是char数组。与上述相同的解决方案。
更好的解决方案是使用托管数组字符(即字符串)
std::string userEntry;
std::getline(std::cin,userEntry);
无需分配或删除,并根据需要重新调整大小,因此您无法注销然后结束。
在C ++代码中看到这一点使我的nech背后的野兔站起来:
typedef void (*WordDefFunc)(const char *word, const char *definition);
在C ++中,使用接口而不是函数指针要容易得多。 (是的,我知道C ++没有关键字接口。但是接口的概念在所有语言中都是相同的,它是实现特定合同的类定义。)
class IFuncAction
{
virtual void action(char const* word,char const* definition) = 0;
};
你在课堂上做了太多的记忆管理 我已经可以看到几个内存泄漏(如果我看起来更难,我相信我会发现指向超出范围的非现有对象的指针)。
现代C ++程序中只有很少的指针。
如果你停止尝试进行手动内存管理,我会打赌你的大多数问题都会消失,就像这是一个C程序。
理想情况下,字典构造函数woudl看起来像这样:
// Pass a stream to the constructor.
// Now a dictionary can be created from a file or
// a string (stringstream) when doing unit tests.
//
Dictionary::Dictionary(std::istream& data)
{
if (!data)
{ throw MyProblemException("Bad Input to Dictionary constructor");
}
Entry item;
// While we can read data from the stream
while(data >> item)
{
// Add it to the store
m_DictEntry.push_back(item);
}
}
std::istream& operator>>(std::istream& str,Entry& data)
{
std::getline(str,data.m_word, COMMA);
std::getline(str,data.m_definition);
return str;
}
答案 1 :(得分:0)
您为用户输入分配单个字符,而它可能要大得多。请改用char *userEntry = new char[MAX_PATH]
答案 2 :(得分:0)
上一篇文章选择了段错误的可能原因,您需要INPUT_LINE_MAX_LEN + 1个字节来存储输入。但是在这个函数中,首先没有理由nto new / delete,只需使用char userEntry [INPUT_LINE_MAX_LEN + 1];在堆栈上
另请注意,您确实有内存泄漏 - Entry类拥有指针,但不会删除它们。由于您没有复制任何内容,因此只需将删除语句添加到此类即可。
我希望这个分配的目的是为了说明为什么我们在现代C ++中避免使用new [] / delete []和同类。如果这是一门Java课程,那就像在AWT 1.0中编写一些东西,只是为了证明它为什么在几年前被替换了。事情是,许多教师都是教师,因为他们可以宣称已经使用C ++“超过15年”。更多的,往往不是伤心地说,这意味着“我写C ++就好像它是1995年。” 为优秀的初学者选择“Accelerated C ++”到使用这些库的C ++。