Open address hashtable segmentation fault - c++

Iam trying to create an open addressed hash table. The user has to be able to define the table's size. For my collision resolution I am using psuedo random probing -
http://algoviz.org/OpenDSA/Books/OpenDSA/html/HashCImproved.html#HashingPseudoRandomProbePRO
Anyway the hashing function(s) seem to work fine until I only have 1 more space to fill in the table. As soon as I input the final element of the table my program crashes and I get a segmentation fault. I've been pulling my hair out trying to find the problem for hours. Please help!
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <vector>
using namespace std;
class HashTable
{
string* hash;
bool* inTable;
vector<int> perm;
int table_size;
int permFunc(int, int);
void permutation();
int hashFunc(string);
public:
//constructor for a hashtable
HashTable(int);
//deconstructor for hashtable
~HashTable();
bool isFull();
//The prototype for the insert method
void insert(string);
//The prototype for the lookup method
bool lookup(string);
//The prototype for the remove method
void remove(string);
//The prototype for the size method
int size();
//The prototype for the print method
void print();
};
//constructor
HashTable::HashTable(int table_size)
{
this -> table_size = table_size;
hash = new string[table_size];
inTable = new bool[table_size];
for(int i=0; i<table_size; i++) perm.push_back(i);
permutation();
}
//deconstructor
HashTable::~HashTable()
{
delete [] hash;
delete [] inTable;
}
//creates a permutation of the perm vectors index elements
void HashTable::permutation()
{
random_shuffle ( perm.begin() + 1, perm.end() );
}
//Hash function method for strings
int HashTable::hashFunc(string str)
{
int key = 0;
for (int i=0; i < str.size(); i++)
{
key += static_cast<int>(str.at(i));
}
cout << "key is:" << key << endl;
return key%table_size;
}
int HashTable::permFunc(int index, int i)
{
int temp = index;
index += perm[i];
if(index > table_size){index = index - table_size;}
if(inTable[index] == false ){return index;}
index = temp;
i++;
permFunc(index, i);
}
bool HashTable::isFull()
{
int num=0;
for(int i=0;i<table_size; i++)
{
if(inTable[i] == true){num ++;}
if(num == table_size){
cout<<"FULL TABLE";
return true;}
}
return false;
}
//Method that inserts an integer into the hash table
void HashTable::insert(string str)
{
int i=1;
if(isFull()){return;}
int pos = hashFunc(str);
if(inTable[pos] == true)
{
int permPos = permFunc(pos, i);
hash[permPos] = str;
inTable[permPos] = true;
}
else
hash[pos] = str;
inTable[pos] = true;
}
//A function that prints out the menu
void menu()
{
cout << endl;
cout << "press i to insert an element into the hash table" << endl;
cout << "press d to inTableete an element from the hash table" << endl;
cout << "press l to look up an element" << endl;
cout << "press s to obtain the size of the table" << endl;
cout << "press p to print the current table" << endl;
return;
}
//The main method
//Implements all the input and output
int main(void)
{
int table_size;
cout << "Enter table size: ";
cin >> table_size;
HashTable h(table_size);
while(true)
{
char c;
string str;
menu();
getline(cin, str);
stringstream stream;
stream << str;
stream >> c;
switch(c)
{
case 'i':
getline(cin, str);
h.insert(str);
break;
case 'd':
getline(cin, str);
h.remove(str);
break;
case 'l':
getline(cin, str);
cout << h.lookup(str) << "\n";
break;
case 's':
cout << h.size() << "\n";
break;
case 'p':
h.print();
break;
default:
cout << "Don't understand '" << c << "'\n";
}
}
return 0;
}

I don't know if this is your problem but this line:
if(index > table_size){index = index - table_size;}
will leave index at table_size which is out of bounds for your array. Change the > to >=
If this isn't it, could you add an example crashing input to your post, for testing?

Related

Conversion of string to char in c++

I am trying to build a spell check validator using Hash Table. I have list of words in a text file. I want to imported them to program and entered them into Hash Table using seperate chaining. Now, I want to run the program and I have these two errors. Can anyone help me with this?
at line 30-- no matching function for call to
'std::__cxx11::basic_string::push_back(std::__cxx11::string&)'
at line 40-- no match for 'operator==' (operand types are
'__gnu_cxx::__alloc_traits, char>::value_type'
{aka 'char'} and 'std::__cxx11::string' {aka
'std::__cxx11::basic_string'})
I know it's simple mistake of converting str to char but I couldn't figure out how to do without changing the rest of the program.
I would like to have a simple solution which doesnot change the existing code.
If it is not possible please tell me how to proceed.
#include<iostream>
#include <string>
#include <cstring>
#include <fstream>
std::string hashTable[27];
int hashTableSize = 27;
#define MAX_LEN 27
using namespace std;
int hashFunc(std::string s)
{
// A simple hashing, no collision handled
int sum=0,index=0;
for(std::string::size_type i=0; i < s.length(); i++)
{
sum += s[i];
}
index = sum % MAX_LEN;
return index;
}
void insert(std::string s)
{
// Compute the index using Hash Function
int index = hashFunc(s);
// Insert the element in the linked list at the particular index
hashTable[index].push_back(s);
}
void search(string s)
{
//Compute the index by using the hash function
int index = hashFunc(s);
//Search the linked list at that specific index
for(int i = 0;i < hashTable[index].size();i++)
{
if(hashTable[index][i] == s)
{
cout << s << " is found!" << endl;
return;
}
}
cout << s << " is not found!" << endl;
}
int main(){
//opening text file
std::ifstream inFile;
inFile.open("un.txt");
// If text file doesnot exist or not included in root folder.
if(inFile.fail()) {
std::cerr << "Error opening file"<< std::endl ;
exit(1);
}
//if text file exists.
std::string wordsinfile;
std::string words[100];
int count=0,i=0;
std::string str;
// writing words from text file into Array.
while( !inFile.eof()) {
inFile >> wordsinfile;
words[i]=wordsinfile;
count++;
i++;
}
for (i=0;i<100;i++){
std::cout<< words[i]<<std::endl;
}
for(i=0;i<=23;i++) {
insert(words[i]);
}
int choice;
string z;
string y;
while(1) {
cout << "Enter choice. 1) Insert\n 2) Search\n 3) Exit\n";
cin >> choice;
switch (choice) {
case 1:
cin>>y;
insert(y);
break;
case 2:
cin>>z;
search(z);
break;
case 3:
exit(0);
}
}
return 0;
}
My txt file had 38 different words and size of hash table is 27
Here's the correct version of your program. Your hashtable is supposed to be a collection of strings and since you are using indexing in the search function, using an STL vector for hashtable should do the trick.
#include <iostream>
#include <string>
#include <cstring>
#include <fstream>
#include <vector>
std::vector<std::string> hashTable[27];
int hashTableSize = 27;
#define MAX_LEN 27
using namespace std;
int hashFunc(std::string s)
{
// A simple hashing, no collision handled
int sum=0,index=0;
for(std::string::size_type i=0; i < s.length(); i++)
{
sum += s[i];
}
index = sum % MAX_LEN;
return index;
}
void insert(std::string s)
{
// Compute the index using Hash Function
int index = hashFunc(s);
// Insert the element in the linked list at the particular index
hashTable[index].push_back(s);
}
void search(string s)
{
//Compute the index by using the hash function
int index = hashFunc(s);
//Search the linked list at that specific index
for(int i = 0;i < hashTable[index].size();i++)
{
if(hashTable[index][i] == s)
{
cout << s << " is found!" << endl;
return;
}
}
cout << s << " is not found!" << endl;
}
int main(){
//opening text file
std::ifstream inFile;
inFile.open("un.txt");
// If text file doesnot exist or not included in root folder.
if(inFile.fail()) {
std::cerr << "Error opening file"<< std::endl ;
exit(1);
}
//if text file exists.
std::string wordsinfile;
std::vector<std::string> words;
std::string str;
// writing words from text file into Array.
while( inFile >> wordsinfile) {
words.push_back(std::move(wordsinfile));
}
std::cout << "Total words read: " << words.size() << std::endl;
for (int i = 0;i < words.size(); i++){
std::cout << words[i] << std::endl;
}
for(int i=0; i < words.size(); i++) {
insert(words[i]);
}
int choice;
string z;
string y;
while(1) {
cout << "Enter choice. 1) Insert\n 2) Search\n 3) Exit\n";
cin >> choice;
switch (choice) {
case 1:
cin>>y;
insert(y);
break;
case 2:
cin>>z;
search(z);
break;
case 3:
exit(0);
}
}
return 0;
}

Extra string outputs if user input is more than one word

This is my first time asking a question on here, so be gentle lol. I wrote up some code for an assignment designed to take information from a (library.txt datatbase) file, store it in arrays, then access/search those arrays by title/author then output that information for the user based on what the user enters.
The issue I am having is, whenever the user enters in a search term longer than one word, the output of "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: " is repeated several times before closing.
I am just looking to make this worthy of my professor lol. Please help me.
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;
struct Book
{
string title;
string author;
};
int loadData(string pathname);
char switchoutput();
void showAll(int count);
int showBooksByAuthor(int count, string name);
int showBooksByTitle(int count, string title);
int FindAuthor(int count, string userinput);
int FindTitle(int count, string userinput);
void ConvertStringToLowerCase(const string orig, string& lwr); //I found this program useful to convert any given string to lowercase, regardless of user input
const int ARRAY_SIZE = 1000;
Book books[ARRAY_SIZE];
int main()
{
string pathname;
string name;
string booktitle;
int count = 0;
int counta = 0;
int countt = 0;
char input = 0;
cout << "Welcome to Jacob's Library Database." << endl;
cout << "Please enter the name of the backup file: " ;
cin >> pathname;
count = loadData(pathname);
cout << count << " records found in the database." << endl;
while (toupper(input != 'Q'))
{
input = switchoutput(); // function call for switchoutput function
switch (input)
{
case 'A':
cout << "Author's Name: ";
cin >> name;
counta = showBooksByAuthor(count, name);
cout << counta << " records found." << endl;
break;
case 'T':
cout << "Book Title: ";
cin >> booktitle;
countt = showBooksByTitle(count, booktitle);
cout << countt << " records found." << endl;
break;
case 'S':
showAll(count);
break;
case 'Q':
break;
}
}
//Pause and exit
cout << endl << "Press 'ENTER' to quit";
getchar();
getchar();
return 0;
}
int loadData(string pathname) //loading data into the array of structs
{
ifstream inFile;
inFile.open(pathname);
if (!inFile) {
cout << "Error, could not read into file. Please re-compile." << endl;
system("PAUSE");
exit(1); //if not in file, exit;
}
int i = 0;
while (!inFile.eof()) {
getline(inFile, books[i].title);
getline(inFile, books[i].author);
i++;
}
return i;
}
char switchoutput() //seperate output function to get my characteroutput constantly resetting and returning the uppercase version for my switch
{
char input;
cout << "Enter Q to (Q)uit, Search (A)uthor, Search (T)itle, (S)how All: ";
cin >> input;
return toupper(input);
}
int showBooksByAuthor(int count, string name)
{
int authorcount = 0;
authorcount = FindAuthor(count, name);
return authorcount;
}
int showBooksByTitle(int count, string title)
{
int titlecount = 0;
titlecount = FindTitle(count, title);
return titlecount;
}
void showAll(int count)
{
for (int i = 0; i < count; i++)
{
cout << books[i].title << " (" << books[i].author << ")" << endl;
}
}
int FindAuthor(int count, string userinput)
{
int authorcount = 0;
string stringlower, arraylower;
int num;
// called upon function to lowercase any of the user inputs
ConvertStringToLowerCase(userinput, stringlower);
for (int i = 0; i < count; ++i) //this function's count determines at which locations to output the author and names (an argument from books by author)
{
// called upon function to lowercase any of the stored authors'
ConvertStringToLowerCase(books[i].author, arraylower);
num = arraylower.find(stringlower); // searches string for userinput (in the lowered array) and stores its value
if (num > -1) // you can never get a -1 input value from an array, thus this loop continues until execution
{
cout << books[i].title << " (" << books[i].author << ")" << endl; //cout book title and book author
authorcount++; //count
}
}
return authorcount;
}
int FindTitle(int count, string userinput) //same as previous but for titles
{
int titlecount = 0;
string stringlower, arraylower;
int num;
ConvertStringToLowerCase(userinput, stringlower);
for (int i = 0; i < count; ++i)
{
ConvertStringToLowerCase(books[i].title, arraylower);
num = arraylower.find(stringlower);
if (num > -1)
{
cout << books[i].title << " (" << books[i].author << ")" << endl;
titlecount++; //count
}
}
return titlecount;
}
void ConvertStringToLowerCase(const string orig, string& lwr) // I found this from another classmate during tutoring, I thought to be useful.
{
lwr = orig;
for (int j = 0; j < orig.length(); ++j) //when called upon in my find functions, it takes the string and convers the string into an array of lowercase letters
{
lwr[j] = tolower(orig.at(j));
}
}

Hash Tables seg fault

So I'm really not sure what is causing the seg fault but I have a feeling that it has something to do with hashTables since the debugger shows it seg faults right at this line its involved with.
Here is the code that is causing the seg fault
//Constructor for hashtable
HashTable::HashTable()
{
for(int i = 0; i < 10; i++)
{
hashTable[i] = new Movie;
hashTable[i]->title = "empty";
hashTable[i]->year = 0;
hashTable[i]->next = NULL;
}
}
//destructor for hashtable
HashTable::~HashTable()
{
}
// this will take a string and convert the letters to hash numbers then add them all together and divide by the hash table size to get a index
int HashTable::initHash(std::string in_title)
{
int hashT = 0;
int index = 0;
for(int i = 0; i < in_title.length(); i++)
{
hashT = hashT + (int)in_title[i];
std::cout << "hash = " << hashT << std::endl;
}
index = hashT % 10;
std::cout << "index = " << index << std::endl;
return index;
}
//This is where we will be inserting a new Movie into the hashtable,
//it will first use initHash to find the number of where it should go in the hash and then from there add it to the hashtable
void HashTable::insertMovie(std::string in_title, int year)
{
int index = initHash(in_title);
std::cout << "index = " << index << std::endl;
if (hashTable[index]->title == "empty") // *** seg faults right here ***
{
hashTable[index]->title = in_title;
hashTable[index]->year = year;
}
else
{
Movie* Ptr = hashTable[index];
Movie* n = new Movie;
n->title = in_title;
n->year = year;
n->next = NULL;
while(Ptr->next != NULL)
{
Ptr = Ptr->next;
}
Ptr->next = n;
}
}
in each of my functions containing hashTables[index] it seg faults but I'm not exactly sure why, the index comes back with a number that should work. does anyone know why this would happen?
edit: Ok here is the two classes that matter from my header file
struct Movie{
std::string title;
int year;
Movie *next;
Movie(){};
Movie(std::string in_title, int in_year)
{
title = in_title;
year = in_year;
}
};
class HashTable
{
public:
HashTable();
~HashTable();
void insertMovie(std::string in_title, int year);
int initHash(std::string in_title);
int NumberofItemsInIndex(int index);
Movie* findMovie(std::string in_title/*, int *index*/);
void deleteMovie(std::string in_title);
void printInventory();
void PrintItemsInIndex(int index);
protected:
private:
Movie **hashTable;
};
edit 2: Here is the main() function
int main(int argc, char * argv[])
{
// Declarations
int input; // Declaring an input for the menu
bool quit = false; // Bool for the menu
//string title; // input value for certain actions
//int year; // input value for certain actions
HashTable *ht;
//int index;
//readFileIntoHash(ht, argv[1]);
while(quit != true)
{
displayMenu(); // Displays the main menu
cin >> input;
//clear out cin
cin.clear();
cin.ignore(10000, '\n');
switch (input)
{
// Insert a movie
case 1:
{
string in_title;
int year;
cout << "Enter Title:" << endl;
cin >> in_title;
cout << "Enter Year:" << endl;
cin >> year;
ht -> insertMovie(in_title, year);
break;
}
// Delete a movie
case 2:
{
string in_title2;
cout << "Enter Title:" << endl;
cin >> in_title2;
ht -> deleteMovie(in_title2);
break;
}
// Find a movie
case 3:
{
string in_title3;
cout << "Enter Title:" << endl;
cin >> in_title3;
ht -> findMovie(in_title3);
break;
}
// Print table contents
case 4:
ht -> printInventory();
break;
case 5:
cout << "Goodbye!" << endl;
quit = true;
break;
// invalid input
default:
cout << "Invalid Input" << endl;
cin.clear();
cin.ignore(10000,'\n');
break;
}
}
return 0;
}
void displayMenu()
{
cout << "======Main Menu=====" << endl;
cout << "1. Insert movie" << endl;
cout << "2. Delete movie" << endl;
cout << "3. Find movie" << endl;
cout << "4. Print table contents" << endl;
cout << "5. Quit" << endl;
return;
}
I added displayMenu() for clarity's sake.
Modify the class definition:
class HashTable
{
public:
HashTable();
~HashTable();
void insertMovie(std::string in_title, int year);
int initHash(std::string in_title);
int NumberofItemsInIndex(int index);
Movie* findMovie(std::string in_title/*, int *index*/);
void deleteMovie(std::string in_title);
void printInventory();
void PrintItemsInIndex(int index);
protected:
private:
Movie *hashTable[10]; /*<<-- since your size is fixed use an array*/
};
Also.. since you allocated Movies in the constructor, remember to deallocate them on destructor:
//destructor for hashtable
HashTable::~HashTable()
{
for(int i = 0; i < 10; i++)
{
delete hashTable[i];
}
}
Alternative, use dynamically allocated memory (with your same class definition)
HashTable::HashTable()
{
hashTable = new (Movie*) [10];
for(int i = 0; i < 10; i++)
{
hashTable[i] = new Movie;
hashTable[i]->title = "empty";
hashTable[i]->year = 0;
hashTable[i]->next = NULL;
}
}
and
HashTable::~HashTable()
{
for(int i = 0; i < 10; i++)
{
delete hashTable[i];
}
delete[] hashTable;
}
Additional fix:
modify main function:
int main(int argc, char * argv[])
{
// Declarations
int input; // Declaring an input for the menu
bool quit = false; // Bool for the menu
//string title; // input value for certain actions
//int year; // input value for certain actions
HashTable ht; // <<==== local variable, not a pointer!
... and then replace ht->xxxx(...) for ht.xxxx(...) elsewhere.
hashTable[i] = new Movie; there you allocate memory for Movie
but memory for Movie **hashTable; was not alllocated
try to add hashTable = new Movie *[100]; to constructor
This constructor perhaps:
HashTable::HashTable()
{
hashTable = new (Movie*) [10]; // Allocate memory on the heap for the array
for(int i = 0; i < 10; i++)
{
hashTable[i] = new Movie;
hashTable[i]->title = "empty";
hashTable[i]->year = 0;
hashTable[i]->next = NULL;
}
}
However, there is actually no need have an array of Movie pointers in your hashmap, you could simply have an array of Movie objects instead. Actually, you want a linked list so my bad. Also, I would not write 10 everywhere, rather use a constant so you could change the size later.
When you get this to work, you should write a destructor to avoid memory leaks. Hint: couple each call to new with a call to delete (and new[] with delete[]).

C++ crashing when passing pointer to function

what i am trying to do is display my pointer to an array of objects. here is my main program, the function it crashes at is listAll(). if i only enter one object it works, but when i enter a second one it crashes. i am at a loss of what is going wrong. problem occurs after picking menuOption 1 twice and then trying to call list all. i see that the array size is not being increased..
#include <fstream>
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
#include "fileStuff.h"
bool menu();
bool menuOptions(int option);
void fileIO();
void listAll(interact * obs, int arryO);
int main()
{
bool isRunning = true;
while (isRunning)
{
isRunning = menu();
}
return 0;
}
bool menu()
{
int option = 0;
cout << "1: add new backpack. " << endl
<< "2: delete a backpack "<< endl
<< "3: sort ascending by id " << endl
<< "4: sort descending by id " << endl
<< "5: list all backpacks " << endl
<< "6: quit" << endl;
cin >> option;
return menuOptions(option);
}
bool menuOptions(int option)
{
static int arrayO = 0;
static interact *obs = new interact[arrayO];
fileStuff test;
int tempBagId = 0, tempInvSpaces = 0, tempAmtOfItemsInInv = 0;
double tempInvMaxWeight = 0.0;
string tempBagType, tempBagCondish;
int t = 0 ;
int i = 0;
switch (option)
{
case 1:
cout << "bagId? ";
cin >> tempBagId;
cout << "How many inv spaces? ";
cin >> tempInvSpaces;
cout << "How much weight can the bag hold? ";
cin >> tempInvMaxWeight;
(obs + arrayO)->setBagId(tempBagId);
(obs + arrayO)->setInvSpaces(tempInvSpaces);
(obs + arrayO)->setInvMaxWeight(tempInvMaxWeight);
cout << "all stored" << endl;
arrayO++;
break;
case 2:
//listmanager delete one
//arrayO--;
break;
case 3:
//sort ascending by id
break;
case 4:
//sort descending by id
break;
case 5:
//list all
listAll(obs, arrayO);
break;
case 6:
obs = NULL;
delete obs;
return false;
break;
default:
break;
}
}
void listAll(interact * obs, int arryO)
{
int i = 0;
cout << i << endl;
cout << arryO << endl;
}
below is the gists of my class.
#include "listManager.h"
#include <iostream>
#include <string>
using namespace std;
interact::interact()
{
bagId = 0;
invSpaces = 0;
invMaxWeigt = 0;
}
void interact::setBagId(int id)
{
bagId = id;
}
void interact::setInvSpaces(int spaces)
{
invSpaces = spaces;
}
void interact::setInvMaxWeight(double weight)
{
invMaxWeigt = weight;
}
int interact::getBagId()
{
return bagId;
}
int interact::getInvSpaces()
{
return invSpaces;
}
double interact::getInvMaxWeight()
{
return invMaxWeigt;
}
You have:
static int arrayO = 0;
static interact *obs = new interact[arrayO];
This will create a dynamic array of 0 length. As Ben stated, the pointer will never change.
The crash is probably caused by trying to access it after increasing the index (arrayO++;), after that it will just be accessing out of bounds memory.
Your obs points to an array with size zero:
static int arrayO = 0;
static interact *obs = new interact[arrayO];
and never is changed to point to anything larger.
Therefore every attempt to subscript it is an array overrun.

C++ - pointer being freed was not allocated error

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