Novice C++ user trying to make a program that uses arrays to store names. The output is a menu that you enter a command and it does said function. The problem is that when I attempt to enter a command from the menu, the program crashes. I don't get any errors or warnings when compiling so I'm at a loss for why it crashes.
Any feedback / help is greatly appreciated
#include <iostream>
#include <string>
using namespace std;
//Prototypes----------------------------------------------------------------------------------------------------------------------------------------------------------------
void addName (string nameList[], const int listMax); //Adds a name
void removeName (string nameList[], const int listMax); //Removes a name
void findName (string nameList[], const int listMax); // Searches for a name in the saved list
void showList (string nameList[], const int listMax); //Shows a list of names currentllistMax stored
void sortList (string nameList[], const int listMax); //Sorts saved names in alphabetical order
//Global Declartions--------------------------------------------------------------------------------------------------------------------------------------------------------
const int listMax = 50;
// MAIN FUNCTION -------------------------------------------------------------------------------------------------------------------------------------------------------------
int main(){
char input; // Variable for when the user inputs their menu selection
string nameList[listMax]; //string called names with the size being namesMax which is 50
cout << "Enter a Command " << endl; //Prompt to choose command from menu
cout << "<A> - Add a name to the database(Max 10)." << endl; //getNames
cout << "<R> - Remove a name from the database" << endl; //removeName
cout << "<F> - Search for a name in the database" << endl; //findName
cout << "<L> - Shows current state of list" << endl; //displalistMaxNames
cout << "<S> - Sorts the database." << endl; //sortNames
cout << "<Q> - Ends the program." << endl; //Exits the program entirellistMax
cin >> input; //Saves the menu choice input
if (input == 'A' ||input == 'a')
{
addName(nameList, listMax);
}
else if (input == 'R' || input == 'r')
{
removeName(nameList, listMax);
}
else if (input == 'F' || input == 'f')
{
findName(nameList, listMax);
}
else if (input == 'L' || input == 'l')
{
showList(nameList, listMax);
}
else if (input == 'S' || input == 's')
{
sortList(nameList, listMax);
}
else if (input == 'Q' || input == 'q')
{
return 0;
}
}
// ADD NAMES FUNCTION --------------------------------------------------------------------------------------------------------------------------------------------------------
void addName(string nameList[], const int listMax){
int x;
for (x = 0; x < listMax; x++) //x = 0 > if x is less than listMax, increment blistMax 1
{
cout << "Enter a name to be added to the database: ";
cin >> nameList[listMax];
}
return;
}
// REMOVE NAMES FUNCTION -----------------------------------------------------------------------------------------------------------------------------------------------------
void removeName(string nameList[], const int listMax){
string clearName; //EmptlistMax String
string removeName;
cout << "Enter the name to be removed from the database: ";
cin >> removeName; //The name entered saved to removeName for further computation
for (int x = 0; x < listMax; x++) // x = 0, if x is less than listMax; increment blistMax 1
{
if(nameList[x] == removeName) // Goes through everlistMax arralistMax level searching to see if it compares to the name saved to removeName
{
nameList[x] = clearName; // If name is found, assign value of clearnName, which is an emptlistMax string, thus removing it from the arralistMax.
}
else if(x == listMax - 1) // If it goes through the entire arralistMax and doesnt find a match it outputs that the name could not be found.
{
cout << "Name not found";
}
}
return;
}
// SEARCH FUNCTION -----------------------------------------------------------------------------------------------------------------------------------------------------------
void findName(string nameList[], const int listMax){
int x = 0;
int z;
bool f = false;
string searchName;
cout << "Enter a name to search for";
cin >> searchName;
while (!f && x <= listMax) //While f and x are equal to / less than listMax
{
int listMax = (x + z) / 2;
if (nameList[listMax] == searchName) // if our arralistMax is on level listMax then its on the list
{
f = true;
cout << nameList[listMax] << "is on the list!" <<endl;
}
else if (nameList[listMax] > searchName)
z = listMax - 1;
else
x = listMax + 1;
}
return;
}
// DISPLAY FUNCTION ----------------------------------------------------------------------------------------------------------------------------------------------------------
void showList(string nameList[], const int listMax){
cout << nameList[listMax];
return;
}
// SORT NAMES FUNCTION -------------------------------------------------------------------------------------------------------------------------------------------------------
void sortList(string nameList[], const int listMax){
bool sort;
do
{
sort = 0;
for (int x = 0; x < listMax - 1; x++)
{
if (nameList[x] > nameList[x + 1])
{
nameList[x].swap(nameList[x+1]);
sort = 1;
}
}
}
while (sort == 1);
return;
}
VisualStudio tells me the code crashes for accessing not-allocated memory.
You use list[listMax] several times, which shouldn't be and isn't OK.
The last item in an array a with length len is a[len-1], not a[len] (Because array indices start from 0 in C).
By the way, it also tells me that in findName() you use z while it's uninitialized, which is undefined behavior.
Related
I have been trying to get this to work for an hour now and I know it can't be that difficult a fix. Every time I enter the file.txt name it comes up as invalid file. I tried moving the files to the same directory as the .cpp file and everything but I just cant get it to read. Help would be appreciated.
#include<iostream>
#include<iomanip>
#include<fstream>
#include<string>
#include<assert.h>
using namespace std;
const int SIZE = 20;
void readIntFile(ifstream& x, int intArray[], int size, int &length);
void printValues(int intArray[], int& length);
char getSentinel();
int main()
{
ifstream inputStream;
const int size = SIZE;
string fileName;
int length = 0;
bool isEmpty = false;
int intArray[size];
char sentinel = 'y';
while (sentinel == 'y' || sentinel == 'Y')
{
cout << "Please enter the name of the file: ";
cin >> fileName;
inputStream.open(fileName);
if (inputStream.bad() || inputStream.fail())
{
cout << "Error, <" << fileName << "> is Invalid File Name.";
}
if (fileName.empty())
{
isEmpty = true;
}
if (isEmpty == true)
{
cout << "Error <" << fileName << "> has no data.";
}
if (inputStream.good() && isEmpty == false)
{
readIntFile(inputStream, intArray, size, length);
printValues(intArray, length);
inputStream.close();
}
sentinel = getSentinel();
}
return 0;
}
void readIntFile(ifstream& x, int intArray[], int size, int& length)
{
int count = 0;
int arrayLocation = -1;
int fileInputValue = 0;
x >> fileInputValue;
while (!x.eof())
{
count ++;
if (count > SIZE)
{
cout << "The file has more than <" << SIZE << "> values." << endl;
break;
}
else
{
arrayLocation ++;
intArray[count] = fileInputValue;
x >> fileInputValue;
}
}
}
void printValues(int intArray[], int& length)
{
assert(length > 0);
cout << "<" << length << "> values processed from the file. The values are: ";
for (int i=0; i <= length; i++)
{
cout << intArray[i] << ", ";
}
}
char getSentinel()
{
char userInput = 'n';
bool inputCheck = false;
cout << "Do you wish to process another file (y/n)?" << endl;
cin >> userInput;
do
{
if (userInput == 'y' || userInput == 'Y' || userInput == 'n' || userInput == 'N')
{
inputCheck = true;
}
else
{
cout << "Invalid response: <" << userInput << ">" << endl;
cout << "Do you wish to process another file (y/n)?" << endl;
cin >> userInput;
}
} while (!inputCheck);
return userInput;
}
Your basic problem is in function
void readIntFile(ifstream& x, int intArray[], int size, int& length)
You do not set the output variable length. And you use the wrong index value for your array. Please check.
Additionally there are many other problems in your code.
I will now paste your code, amended with my comments comments, where problems are or where things should be improved.
Please see:
#include<iostream>
#include<iomanip>
#include<fstream>
#include<string>
#include<assert.h>
using namespace std; // Should never be used. Always use fully qualified names
const int SIZE = 20; // Please use constexpr
// Do not use C-Style function prototypes. Put main at the bottom
// Do not use C-Style Arrays
// In C++ (C-Style-)Arrays are different. You actually do not pass an array to your function
// but a decyed pointer. You can pass an array by pointer or by reference, but this has a different syntax
void readIntFile(ifstream& x, int intArray[], int size, int &length);
void printValues(int intArray[], int& length);
char getSentinel();
int main()
{
// All variables shall be initialized at the point of defintion
ifstream inputStream; // You should define variables just before you need then
const int size = SIZE; // This is code duplication. And constexpr should be used
string fileName;
int length = 0;
bool isEmpty = false;
int intArray[size]; // C-Style Arrays should not be used. Use std::array or best, std::vector
char sentinel = 'y'; // You could use universal initializer syntax with braced initializer
while (sentinel == 'y' || sentinel == 'Y')
{
cout << "Please enter the name of the file: ";
cin >> fileName;
inputStream.open(fileName); // The constructor can open the file for you
if (inputStream.bad() || inputStream.fail()) // This is too complicated
{
cout << "Error, <" << fileName << "> is Invalid File Name.";
}
if (fileName.empty()) // Check is too late and not necessary
{
isEmpty = true;
}
if (isEmpty == true)
{
cout << "Error <" << fileName << "> has no data.";
}
if (inputStream.good() && isEmpty == false)
{
readIntFile(inputStream, intArray, size, length);
printValues(intArray, length);
inputStream.close(); // Destructor will clsoe the file for you
}
sentinel = getSentinel();
}
return 0;
}
// Not optimal function prototype
void readIntFile(ifstream& x, int intArray[], int size, int& length)
{
// the whole design / logic is very strange
int count = 0;
int arrayLocation = -1; // More then strange. Shows that this is a bad design
int fileInputValue = 0;
x >> fileInputValue;
while (!x.eof()) // Bad or even wrong design
{
count ++; // Wrong. See below. array will be filled with starting with index one
if (count > SIZE)
{
cout << "The file has more than <" << SIZE << "> values." << endl;
break;
}
else
{
arrayLocation ++; // This variable is not used
intArray[count] = fileInputValue;
x >> fileInputValue;
}
}
// Nobody will set the length variable
}
void printValues(int intArray[], int& length)
{
assert(length > 0); // Basically OK, but no necessary here. Cannoz happen
cout << "<" << length << "> values processed from the file. The values are: ";
for (int i=0; i <= length; i++)
{
cout << intArray[i] << ", ";
}
// There is now newline character used anywhere
}
// Very complicated
char getSentinel()
{
char userInput = 'n';
bool inputCheck = false;
cout << "Do you wish to process another file (y/n)?" << endl;
cin >> userInput;
do
{
if (userInput == 'y' || userInput == 'Y' || userInput == 'n' || userInput == 'N')
{
inputCheck = true;
}
else
{
cout << "Invalid response: <" << userInput << ">" << endl;
cout << "Do you wish to process another file (y/n)?" << endl;
cin >> userInput;
}
} while (!inputCheck);
return userInput;
}
Next, I will make your code working, by fixing the biggest problems. I will still follow your programming style.
#include<iostream>
#include<iomanip>
#include<fstream>
#include<string>
#include<assert.h>
constexpr int MaxArraySize = 20;
using IntArray = int[MaxArraySize];
void readIntFile(std::ifstream& x, int intArray[], int& length)
{
length = 0;
int value{};
while (x >> value)
{
if (length >= MaxArraySize)
{
std::cout << "The file has more than <" << MaxArraySize << "> values.\n";
break;
}
else
{
intArray[length++] = value;
}
}
}
void printValues(int intArray[], int& length)
{
std::cout << "\n<" << length << "> values processed from the file. The values are: ";
for (int i=0; i < length; i++)
{
std::cout << intArray[i] << ", ";
}
std::cout << "\n\n";
}
bool getSentinel()
{
char userInput{'n'};
bool valid = false;
while (not valid)
{
std::cout << "\n\nDo you wish to process another file (y/n)?\n";
std::cin >> userInput;
if (userInput != 'y' && userInput != 'Y' && userInput != 'n' && userInput != 'N')
{
std::cout << "Invalid response: <" << userInput << ">\n\n";
}
else {
valid = true;
}
}
return ( userInput=='y' || userInput=='Y');
}
int main()
{
int intArray[MaxArraySize];
bool sentinel = true;
while (sentinel)
{
std::cout << "Please enter the name of the file: ";
std::string fileName{};
std::cin >> fileName;
if (fileName.empty())
{
std::cout << "Error <" << fileName << "> has no data.\n\n";
}
else {
std::ifstream inputStream(fileName);
if (!inputStream)
{
std::cout << "Error, <" << fileName << "> is Invalid File Name.\n\n";
}
else
{
int length = 0;
readIntFile(inputStream, intArray, length);
printValues(intArray, length);
}
}
sentinel = getSentinel();
}
return 0;
}
and, in the end, because we are in a C++ site here, I will show (one of many possible) a more advanced C++ solution.
This is just for information and to grab some ideas for the future
#include<iostream>
#include<iomanip>
#include<fstream>
#include<string>
#include<vector>
#include<algorithm>
#include<iterator>
#include<initializer_list>
// Some aliases for easier reading and saving typing work
using DataType = int;
using Vector = std::vector<DataType>;
// Define an "in" operator
struct in {
in(const std::initializer_list<char>& il) : ref(il) {}
const std::initializer_list<char>& ref;
};
bool operator,(const char& lhs, const in& rhs) {
return std::find(rhs.ref.begin(), rhs.ref.end(), lhs) != rhs.ref.end();
}
int main() {
// As long as the user wants to read files
for (bool userWantsToContinue{true}; userWantsToContinue;) {
std::cout << "\nPlease enter a valid filename: ";
if (std::string filename{}; std::cin >> filename) {
// Open the file for reading and check, it it is open
if (std::ifstream inputStream{filename}; inputStream) {
// Read all data from the file
Vector data(std::istream_iterator<DataType>(inputStream), {});
// Now show result to user
std::cout << "\nRead values are:\n";
std::copy(data.begin(), data.end(), std::ostream_iterator<DataType>(std::cout, " "));
}
else std::cerr << "\nError: Could not open file '" << filename << "' for reading\n\n";
}
else std::cerr << "\nError: Problem with filename input\n\n";
// Ask, if the user wants to continue
bool validInput{false};
while (not validInput) {
std::cout << "\n\nDo you want to read more files? Please enter 'y' or 'n' ";
if (char selection{}; (std::cin >> selection) && (selection, in{'y','Y','n','N',}) ) {
validInput = true;
userWantsToContinue = (selection, in{'y','Y'});
}
else {
std::cout << "\n\nInvalid input, please retry\n\n";
}
}
}
return 0;
}
Try using an full absolute filename, not a relative one.
And replace cin >> fileName; with std::getline(std::cin, fileName); Otherwise, it will easily break. The getline version is much more robust.
If this was not enough, read the answer of: How do I get the directory that a program is running from? and do the checks suited for your system to find out your "working directory". This is the place where you input file must be, if you don't use absolute paths.
When I run my program, I have to type how many rows do I want in my output. I have a limit from 1 to 100 rows. Each row is a task with a name of the task followed by increasing number, example: Task1:, Task2, .... When I type something into input, it must convert input string /see the code below - except the code in main();/.
My problem is that when I type first input, it should go to next task/next row/ but it doesnt. I type for example 10 strings but they dont go each to next task but they stay in one task..hope you understand now.
#include<iostream>
#include<string>
#include <ctype.h>
using namespace std;
void Convert(string input){
string output = "";
string flag = "";
bool underscore = false;
bool uppercase = false;
if ( islower(input[0]) == false){
cout << "Error!" <<endl;
return;
}
for (int i=0; i < input.size(); i++){
if ( (isalpha( input[i] ) || (input[i]) == '_') == false){
cout << "Error!" <<endl;
return;
}
if (islower(input[i])){
if (underscore){
underscore = false;
output += toupper(input[i]);
}
else
output += input[i];
}
else if (isupper(input[i])){
if (flag == "C" || uppercase){
cout << "Error!"<<endl;
return;
}
flag = "Java";
output += '_';
output += tolower(input[i]);
}
else if (input[i] == '_'){
if (flag == "Java" || underscore){
cout << "Error!" <<endl;
return;
}
flag = "C";
underscore = true;
}
}
cout << output <<endl;
}
int main(){
const int max = 100;
string input;
int pocet_r;
cout << "Zadaj pocet uloh:" << endl;
cin >> pocet_r;
if(pocet_r >= 1 && pocet_r <=100)
{
for (int i = 0; i <pocet_r; i++)
{
cout << "Uloha " << i+1 << ":" << endl;
while (cin >> input)
Convert (input);
while(input.size() > max)
cout << "slovo musi mat minimalne 1 a maximalne 100 znakov" << endl;
while(input.size() > max)
cin >> input;
while (cin >> input)
Convert(input);
}
}else{
cout << "Minimalne 1 a maximalne 100 uloh" << endl;
}
system("pause");
}
Your first if in Convert will always fail on a non-underscore and return. I don't think that's what's intended. Agree with other answer on the while cin loop. The next two whiles should be if's apparently. Step thru this code with a debugger and watch it line by line and see where it fails. You've got multiple issues here, and I'm not entirely sure what the intent is.
Edit - I didn't parse the extra parenthesis correctly. The first if in convert is actually okay.
I have the following code and am trying to figure out how to have the option to return to the main menu by hitting any key, as long as the quit option isn't chosen. I assume something with this while loop is how it is done, but when I execute the code it terminates after 1 iteration. I'm just learning c++ so I'm not quite sure how to navigate through this problem.
//Runs a program with a menu that the user can navigate through different options with via text input
#include <iostream>
#include <cctype>
using namespace std;
int main()
{
char userinp;
cout<<"Here is the menu:" << endl;
cout<<"Help(H) addIntegers(A) subDoubles(D) Quit(Q)";
cin >> userinp;
userinp = tolower(userinp);
int count = 1;
while (count == 1)
{
if (userinp == 'h')
{
cout <<"This is the help menu. Upon returning to the main menu, input A or a to add 2 intergers." << endl;
cout <<"Input D or d to subtract 2 doubles. Input Q or q to quit.";
count = 1;
return count;
}
else if (userinp == 'a')
{
int a, b, result;
cout <<"Enter two integers:";
cin >> a >> b;
result = a + b;
cout << "The sum of " << a << " + " << b << " = " << result;
count = 1;
return count;
}
else if (userinp == 'd')
{
double a, b, result;
cout <<"Enter two integers:";
cin >> a >> b;
result = a - b;
cout << "The difference of " << a << " - " << b << " = " << result;
count = 1;
return count;
}
else if (userinp == 'q')
{
exit(0);
}
else
{
cout <<"Please input a valid character to navigate the menu - input the letter h for the help menu";
cout << "Press any key to continue";
count = 1;
return count;
}
}
}
remove
else
{
cout <<"Please input a valid character to navigate the menu - input the letter h for the help menu";
cout << "Press any key to continue";
count = 1;
return count;
}
and instead of while(count == 1) change it to while(true)//which means continously loop until q is pressed
also have this part inside while loop i.e
while(true)
{
cout<<"Here is the menu:" << endl;
cout<<"Help(H) addIntegers(A) subDoubles(D) Quit(Q)";
cin >> userinp;
userinp = tolower(userinp);
}
to have a prompt for continue:
1.add bool cond =true;(before while(true))
2.change while(true) to while(cond)
3.add this else block after else if (userinp == 'q') block
else
{ char ch;
cout << "Press y to continue";
cin>>ch;
if(ch=='y')
{
cond =true;
}
else
{
cond =false;
exit(0);
}
}
malloc: *** error for object 0x10ee008c0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
Or I get this when I try and print everything
Segmentation fault: 11
I'm doing some homework for an OOP class and I've been stuck for a good hour now. I'm getting this error once I've used keyboard input enough. I am not someone who gets frustrated at all, and here I am getting very frustrated with this. Here are the files:
This is the book class. I'm pretty sure this is very solid. But just for reference:
//--------------- BOOK.CPP ---------------
// The class definition for Book.
//
#include <iostream>
#include "book.h"
using namespace std;
Book::Book()
//
{
strcpy(title, " ");
strcpy(author, " ");
type = FICTION;
price = 0;
}
void Book::Set(const char* t, const char* a, Genre g, double p)
{
strcpy(title, t);
strcpy(author, a);
type = g;
price = p;
}
const char* Book::GetTitle() const
{
return title;
}
const char* Book::GetAuthor() const
{
return author;
}
double Book::GetPrice() const
{
return price;
}
Genre Book::GetGenre() const
{
return type;
}
void Book::Display() const
{
int i;
cout << GetTitle();
for (i = strlen(title) + 1; i < 32; i++)
cout << (' ');
cout << GetAuthor();
for (i = strlen(author) + 1; i < 22; i++)
cout << (' ');
switch (GetGenre())
{
case FICTION:
cout << "Fiction ";
break;
case MYSTERY:
cout << "Mystery ";
break;
case SCIFI:
cout << "SciFi ";
break;
case COMPUTER:
cout << "Computer ";
break;
}
cout << "$";
if (GetPrice() < 1000)
cout << " ";
if (GetPrice() < 100)
cout << " ";
if (GetPrice() < 10)
cout << " ";
/* printf("%.2f", GetPrice());*/
cout << '\n';
}
This is the store class that deals with the array and dynamic allocation. This was working well without input commands, but just using its functions it was working like a champ.
//--------------- STORE.CPP ---------------
// The class definition for Store.
//
#include <iostream>
#include <cstring> // for strcmp
#include "store.h"
using namespace std;
Store::Store()
{
maxSize = 5;
currentSize = 0;
bookList = new Book[maxSize];
}
Store::~Store()
// This destructor function for class Store
// deallocates the Store's list of Books
{
delete [] bookList;
}
void Store::Insert(const char* t, const char* a, Genre g, double p)
// Insert a new entry into the direrctory.
{
if (currentSize == maxSize)// If the directory is full, grow it.
Grow();
bookList[currentSize++].Set(t, a, g, p);
}
void Store::Sell(const char* t)
// Sell a book from the store.
{
char name[31];
strcpy(name, t);
int thisEntry = FindBook(name);// Locate the name in the directory.
if (thisEntry == -1)
cout << *name << " not found in directory";
else
{
cashRegister = cashRegister + bookList[thisEntry].GetPrice();
// Shift each succeding element "down" one position in the
// Entry array, thereby deleting the desired entry.
for (int j = thisEntry + 1; j < currentSize; j++)
bookList[j - 1] = bookList[j];
currentSize--;// Decrement the current number of entries.
cout << "Entry removed.\n";
if (currentSize < maxSize - 5)// If the directory is too big, shrink it.
Shrink();
}
}
void Store::Find(const char* x) const
// Display the Store's matches for a title or author.
{
// Prompt the user for a name to be looked up
char name[31];
strcpy(name, x);
int thisBook = FindBook(name);
if (thisBook != -1)
bookList[thisBook].Display();
int thisAuthor = FindAuthor(name, true);
if ((thisBook == -1) && (thisAuthor == -1))
cout << name << " not found in current directory\n";
}
void Store::DisplayGenre(const Genre g) const
{
double genrePrice = 0;
int genreCount = 0;
for (int i = 0; i < currentSize; i++)// Look at each entry.
{
if (bookList[i].GetGenre() == g)
{
bookList[i].Display();
genrePrice = genrePrice + bookList[i].GetPrice();
genreCount++;
}
}
cout << "Number of books in this genre: " << genreCount
<< " " << "Total: $";
if (genrePrice < 1000)
cout << " ";
if (genrePrice < 100)
cout << " ";
if (genrePrice < 10)
cout << " ";
printf("%.2f", genrePrice);
}
void Store::DisplayStore() const
{
if (currentSize >= 1)
{
cout << "**Title**\t\t"
<< "**Author**\t"
<< "**Genre**\t"
<< "**Price**\n\n";
for (int i = 0; i < currentSize; i++)
bookList[i].Display();
}
else
cout << "No books currently in inventory\n\n";
cout << "Total Books = " << currentSize
<< "\nMoney in the register = $";
if (cashRegister < 1000)
cout << " ";
if (cashRegister < 100)
cout << " ";
if (cashRegister < 10)
cout << " ";
printf("%.2f", cashRegister);
cout << '\n';
}
void Store::Sort(char type)
{
Book temp;
for(int i = 0; i <= currentSize; i++)
{
for (int j = i+1; j < currentSize; j++)
{
if (type == 'A')
{
if (strcmp(bookList[i].GetTitle(), bookList[j].GetTitle()) > 0)
{
temp = bookList[i];
bookList[i] = bookList[j];
bookList[j] = temp;
}
}
if (type == 'T')
{
if (strcmp(bookList[i].GetAuthor(), bookList[j].GetAuthor()) > 0)
{
temp = bookList[i];
bookList[i] = bookList[j];
bookList[j] = temp;
}
}
}
}
}
void Store::SetCashRegister(double x)
// Set value of cash register
{
cashRegister = x;
}
void Store::Grow()
// Double the size of the Store's bookList
// by creating a new, larger array of books
// and changing the store's pointer to refer to
// this new array.
{
maxSize = currentSize + 5;// Determine a new size.
cout << "** Array being resized to " << maxSize
<< " allocated slots" << '\n';
Book* newList = new Book[maxSize];// Allocate a new array.
for (int i = 0; i < currentSize; i++)// Copy each entry into
newList[i] = bookList[i];// the new array.
delete [] bookList;// Remove the old array
bookList = newList;// Point old name to new array.
}
void Store::Shrink()
// Divide the size of the Store's bookList in
// half by creating a new, smaller array of books
// and changing the store's pointer to refer to
// this new array.
{
maxSize = maxSize - 5;// Determine a new size.
cout << "** Array being resized to " << maxSize
<< " allocated slots" << '\n';
Book* newList = new Book[maxSize];// Allocate a new array.
for (int i = 0; i < currentSize; i++)// Copy each entry into
newList[i] = bookList[i];// the new array.
delete [] bookList;// Remove the old array
bookList = newList;// Point old name to new array.
}
int Store::FindBook(char* name) const
// Locate a name in the directory. Returns the
// position of the entry list as an integer if found.
// and returns -1 if the entry is not found in the directory.
{
for (int i = 0; i < currentSize; i++)// Look at each entry.
if (strcmp(bookList[i].GetTitle(), name) == 0)
return i;// If found, return position and exit.
return -1;// Return -1 if never found.
}
int Store::FindAuthor(char* name, const bool print) const
// Locate a name in the directory. Returns the
// position of the entry list as an integer if found.
// and returns -1 if the entry is not found in the directory.
{
int returnValue;
for (int i = 0; i < currentSize; i++)// Look at each entry.
if (strcmp(bookList[i].GetAuthor(), name) == 0)
{
if (print == true)
bookList[i].Display();
returnValue = i;// If found, return position and exit.
}
else
returnValue = -1;// Return -1 if never found.
return returnValue;
}
Now this is the guy who needs some work. There may be some stuff blank so ignore that. This one controls all the input, which is the problem I believe.
#include <iostream>
#include "store.h"
using namespace std;
void ShowMenu()
// Display the main program menu.
{
cout << "\n\t\t*** BOOKSTORE MENU ***";
cout << "\n\tA \tAdd a Book to Inventory";
cout << "\n\tF \tFind a book from Inventory";
cout << "\n\tS \tSell a book";
cout << "\n\tD \tDisplay the inventory list";
cout << "\n\tG \tGenre summary";
cout << "\n\tO \tSort inventory list";
cout << "\n\tM \tShow this Menu";
cout << "\n\tX \teXit Program";
}
char GetAChar(const char* promptString)
// Prompt the user and get a single character,
// discarding the Return character.
// Used in GetCommand.
{
char response;// the char to be returned
cout << promptString;// Prompt the user
cin >> response;// Get a char,
response = toupper(response);// and convert it to uppercase
cin.get();// Discard newline char from input.
return response;
}
char Legal(char c)
// Determine if a particular character, c, corresponds
// to a legal menu command. Returns 1 if legal, 0 if not.
// Used in GetCommand.
{
return((c == 'A') || (c == 'F') || (c == 'S') ||
(c == 'D') || (c == 'G') || (c == 'O') ||
(c == 'M') || (c == 'X'));
}
char GetCommand()
// Prompts the user for a menu command until a legal
// command character is entered. Return the command character.
// Calls GetAChar, Legal, ShowMenu.
{
char cmd = GetAChar("\n\n>");// Get a command character.
while (!Legal(cmd))// As long as it's not a legal command,
{// display menu and try again.
cout << "\nIllegal command, please try again . . .";
ShowMenu();
cmd = GetAChar("\n\n>");
}
return cmd;
}
void Add(Store s)
{
char aTitle[31];
char aAuthor[21];
Genre aGenre = FICTION;
double aPrice = 10.00;
cout << "Enter title: ";
cin.getline(aTitle, 30);
cout << "Enter author: ";
cin.getline(aAuthor, 20);
/*
cout << aTitle << " " << aAuthor << "\n";
cout << aGenre << " " << aPrice << '\n';
*/
s.Insert(aTitle, aAuthor, aGenre, aPrice);
}
void Find()
{
}
void Sell()
{
}
void ViewGenre(Store s)
{
char c;
Genre result;
do
c = GetAChar("Enter Genre - (F)iction, (M)ystery, (S)ci-Fi, or (C)omputer: ");
while ((c != 'F') && (c != 'M') && (c != 'S') && (c != 'C'));
switch (result)
{
case 'F': s.DisplayGenre(FICTION); break;
case 'M': s.DisplayGenre(MYSTERY); break;
case 'S': s.DisplayGenre(SCIFI); break;
case 'C': s.DisplayGenre(COMPUTER); break;
}
}
void Sort(Store s)
{
char c;
Genre result;
do
c = GetAChar("Enter Genre - (F)iction, (M)ystery, (S)ci-Fi, or (C)omputer: ");
while ((c != 'A') && (c != 'T'));
s.Sort(c);
}
void Intro(Store s)
{
double amount;
cout << "*** Welcome to Bookstore Inventory Manager ***\n"
<< "Please input the starting money in the cash register: ";
/* cin >> amount;
s.SetCashRegister(amount);*/
}
int main()
{
Store mainStore;// Create and initialize a Store.
Intro(mainStore);//Display intro & set Cash Regsiter
ShowMenu();// Display the menu.
/*mainStore.Insert("A Clockwork Orange", "Anthony Burgess", SCIFI, 30.25);
mainStore.Insert("X-Factor", "Anthony Burgess", SCIFI, 30.25);*/
char command;// menu command entered by user
do
{
command = GetCommand();// Retrieve a command.
switch (command)
{
case 'A': Add(mainStore); break;
case 'F': Find(); break;
case 'S': Sell(); break;
case 'D': mainStore.DisplayStore(); break;
case 'G': ViewGenre(mainStore); break;
case 'O': Sort(mainStore); break;
case 'M': ShowMenu(); break;
case 'X': break;
}
} while ((command != 'X'));
return 0;
}
Please, any and all help you can offer is amazing.
Thank you.
Welcome to the exciting world of C++!
Short answer: you're passing Store as a value. All your menu functions should take a Store& or Store* instead.
When you're passing Store as a value then an implicit copy is done (so the mainStore variable is never actually modified). When you return from the function the Store::~Store is called to clean up the copied data. This frees mainStore.bookList without changing the actual pointer value.
Further menu manipulation will corrupt memory and do many double frees.
HINT: If you're on linux you can run your program under valgrind and it will point out most simple memory errors.
Your Store contains dynamically-allocated data, but does not have an assignment operator. You have violated the Rule of Three.
I don't see the Store class being instantiated anywhere by a call to new Store() which means the booklist array has not been created but when the program exits and calls the destructor, it tries to remove the array that was never allocated and hence that's why i think you are getting this error. Either, modify the destructor to have a null check or instantiate the class by a call to the constructor. Your code shouldn't still be working anywhere you are trying to use a Store object.
Hope this helps
When I input locations from a txt file I am getting a peculiar error where it seems to miss off the first entry, yet add a garbage entry to the end of the link list (it is designed to take the name, latitude and longitude for each location you will notice). I imagine this to be an issue with where it starts collecting the inputs and where it stops but I cant find the error!! It reads the first line correctly but then skips to the next before adding it because during testing for the bug it had no record of the first location Lisbon though whilst stepping into the method call it was reading it. Very bizarre but hopefully someone knows the issue. Here is firstly my header file:
#include <string>
struct locationNode
{
char nodeCityName [35];
double nodeLati;
double nodeLongi;
locationNode* Next;
void CorrectCase() // Correct upper and lower case letters of input
{
int MAX_SIZE = 35;
int firstLetVal = this->nodeCityName[0], letVal;
int n = 1; // variable for name index from second letter onwards
if((this->nodeCityName[0] >90) && (this->nodeCityName[0] < 123)) // First letter is lower case
{
firstLetVal = firstLetVal - 32; // Capitalise first letter
this->nodeCityName[0] = firstLetVal;
}
while(n <= MAX_SIZE - 1)
{
if((this->nodeCityName[n] >= 65) && (this->nodeCityName[n] <= 90))
{
letVal = this->nodeCityName[n] + 32;
this->nodeCityName[n] = letVal;
}
n++;
}
//cityNameInput = this->nodeCityName;
}
};
class Locations
{
private:
int size;
public:
Locations(){
}; // constructor for the class
locationNode* Head;
//int Add(locationNode* Item);
};
And here is the file containing main:
// U08221.cpp : main project file.
#include "stdafx.h"
#include "Locations.h"
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int n = 0,x, locationCount = 0, MAX_SIZE = 35;
string cityNameInput;
char targetCity[35];
bool acceptedInput = false, userInputReq = true, match = false, nodeExists = false;// note: addLocation(), set to true to enable user input as opposed to txt file
locationNode *start_ptr = NULL; // pointer to first entry in the list
locationNode *temp, *temp2; // Part is a pointer to a new locationNode we can assign changing value followed by a call to Add
locationNode *seek, *bridge;
void setElementsNull(char cityParam[])
{
int y=0, count =0;
while(cityParam[y] != NULL)
{
y++;
}
while(y < MAX_SIZE)
{
cityParam[y] = NULL;
y++;
}
}
void addLocation()
{
temp = new locationNode; // declare the space for a pointer item and assign a temporary pointer to it
if(!userInputReq) // bool that determines whether user input is required in adding the node to the list
{
cout << endl << "Enter the name of the location: ";
cin >> temp->nodeCityName;
temp->CorrectCase();
setElementsNull(temp->nodeCityName);
cout << endl << "Please enter the latitude value for this location: ";
cin >> temp->nodeLati;
cout << endl << "Please enter the longitude value for this location: ";
cin >> temp->nodeLongi;
cout << endl;
}
temp->Next = NULL; //set to NULL as when one is added it is currently the last in the list and so can not point to the next
if(start_ptr == NULL){ // if list is currently empty, start_ptr will point to this node
start_ptr = temp;
}
else
{ temp2 = start_ptr;
// We know this is not NULL - list not empty!
while (temp2->Next != NULL)
{
temp2 = temp2->Next; // Move to next link in chain until reach end of list
}
temp2->Next = temp;
}
++locationCount; // increment counter for number of records in list
if(!userInputReq){
cout << "Location sucessfully added to the database! There are " << locationCount << " location(s) stored" << endl;
}
}
void populateList(){
ifstream inputFile;
inputFile.open ("locations.txt", ios::in);
userInputReq = true;
temp = new locationNode; // declare the space for a pointer item and assign a temporary pointer to it
do
{
inputFile.get(temp->nodeCityName, 35, ' ');
setElementsNull(temp->nodeCityName);
inputFile >> temp->nodeLati;
inputFile >> temp->nodeLongi;
setElementsNull(temp->nodeCityName);
if(temp->nodeCityName[0] == 10) //remove linefeed from input
{
for(int i = 0; temp->nodeCityName[i] != NULL; i++)
{
temp->nodeCityName[i] = temp->nodeCityName[i + 1];
}
}
addLocation();
}
while(!inputFile.eof());
userInputReq = false;
cout << "Successful!" << endl << "List contains: " << locationCount << " entries" << endl;
cout << endl;
inputFile.close();
}
bool nodeExistTest(char targetCity[]) // see if entry is present in the database
{
match = false;
seek = start_ptr;
int letters = 0, letters2 = 0, x = 0, y = 0;
while(targetCity[y] != NULL)
{
letters2++;
y++;
}
while(x <= locationCount) // locationCount is number of entries currently in list
{
y=0, letters = 0;
while(seek->nodeCityName[y] != NULL) // count letters in the current name
{
letters++;
y++;
}
if(letters == letters2) // same amount of letters in the name
{
y = 0;
while(y <= letters) // compare each letter against one another
{
if(targetCity[y] == seek->nodeCityName[y])
{
match = true;
y++;
}
else
{
match = false;
y = letters + 1; // no match, terminate comparison
}
}
}
if(match)
{
x = locationCount + 1; //found match so terminate loop
}
else{
if(seek->Next != NULL)
{
bridge = seek;
seek = seek->Next;
x++;
}
else
{
x = locationCount + 1; // end of list so terminate loop
}
}
}
return match;
}
void deleteRecord() // complete this
{
int junction = 0;
locationNode *place;
cout << "Enter the name of the city you wish to remove" << endl;
cin >> targetCity;
setElementsNull(targetCity);
if(nodeExistTest(targetCity)) //if this node does exist
{
if(seek == start_ptr) // if it is the first in the list
{
junction = 1;
}
if(seek != start_ptr && seek->Next == NULL) // if it is last in the list
{
junction = 2;
}
switch(junction) // will alter list accordingly dependant on where the searched for link is
{
case 1:
start_ptr = start_ptr->Next;
delete seek;
--locationCount;
break;
case 2:
place = seek;
seek = bridge;
delete place;
--locationCount;
break;
default:
bridge->Next = seek->Next;
delete seek;
--locationCount;
break;
}
}
else
{ cout << targetCity << "That entry does not currently exist" << endl << endl << endl;
}
}
void searchDatabase()
{
char choice;
cout << "Enter search term..." << endl;
cin >> targetCity;
if(nodeExistTest(targetCity))
{
cout << "Entry: " << endl << endl;
}
else
{
cout << "Sorry, that city is not currently present in the list." << endl << "Would you like to add this city now Y/N?" << endl;
cin >> choice;
/*while(choice != ('Y' || 'N'))
{
cout << "Please enter a valid choice..." << endl;
cin >> choice;
}*/
switch(choice)
{
case 'Y':
addLocation();
break;
case 'N':
break;
default :
cout << "Invalid choice" << endl;
break;
}
}
}
void printDatabase()
{
temp = start_ptr; // set temp to the start of the list
do
{ if (temp == NULL)
{
cout << "You have reached the end of the database" << endl;
}
else
{ // Display details for what temp points to at that stage
cout << "Location : " << temp->nodeCityName << endl;
cout << "Latitude : " << temp->nodeLati << endl;
cout << "Longitude : " << temp->nodeLongi << endl;
cout << endl;
// Move on to next locationNode if one exists
temp = temp->Next;
}
}
while (temp != NULL);
}
void nameValidation(string name)
{
n = 0; // start from first letter
x = name.size();
while(!acceptedInput)
{
if((name[n] >= 65) && (name[n] <= 122)) // is in the range of letters
{
while(n <= x - 1)
{
while((name[n] >=91) && (name[n] <=97)) // ERROR!!
{
cout << "Please enter a valid city name" << endl;
cin >> name;
}
n++;
}
}
else {
cout << "Please enter a valid city name" << endl;
cin >> name;
}
if(n <= x - 1)
{
acceptedInput = true;
}
}
cityNameInput = name;
}
int main(array<System::String ^> ^args)
{
//main contains test calls to functions at present
cout << "Populating list...";
populateList();
printDatabase();
deleteRecord();
printDatabase();
cin >> cityNameInput;
}
The text file contains this (ignore the names, they are just for testing!!):
Lisbon 45 47
Fattah 45 47
Darius 42 49
Peter 45 27
Sarah 85 97
Michelle 45 47
John 25 67
Colin 35 87
Shiron 40 57
George 34 45
Sean 22 33
The output omits Lisbon, but adds on a garbage entry with nonsense values. Any ideas why? Thank you in advance.
The main function creates a new locationNode and stores it in the global variable temp, then reads the first dataset and stores it in that node.
Then you call addLocation() which starts by creating another new locationNode which replaces the existing one in temp. This new node is then inserted into the list.
The next iteration of the main loop then fills that temp value with values and addLocation() inserts another brand new locationNode into the list. So the first dataset isn't stored in the list and each iteration ends up by inserting an uninitialized new node.
As you see using global variables can lead to confusing situations and you code would surely become clearer if you were passing the nodes as parameters to your functions.