Can't modify a string in C++ array - c++

Trying to learn datastructures, I made this class for a stack. It works just fine with integers but it throws a mysterious error with strings.
The class List is the API for my stack. Its meant to resize automatically when it reaches the limit. The whole code is just for the sake of learning but the error I get doesn't make any sense and it happens somewhere in some assembly code.
#include <iostream>
#include<string>
using namespace std;
class List {
private:
int N = 0;
string* list = new string[1];
void resize(int sz) {
max = sz;
string* oldlist = list;
string* list = new string[max];
for (int i = 0; i < N; i++) {
list[i] = oldlist[i];
}
}
int max = 1;
public:
void push(string str) {
if (N == max) {
resize(2 * N);
}
cout << max << endl;
list[N] = str;
N++;
}
void pop() {
cout << list[--N] << endl;
}
};
int main()
{
string in;
List list;
while (true) {
cin >> in;
if (in == "-") {
list.pop();
}
else {
list.push(in);
}
}
}

string* list = new string[max]; in the resize method defines a new variable named list that "shadows", replaces, the member variable list. The member list goes unchanged and the local variable list goes out of scope at the end of the function, losing all of the work.
To fix: Change
string* list = new string[max];
to
list = new string[max];
so that the function will use the member variable.
Don't forget to delete[] oldlist; when you're done with it to free up the storage it points at.

Related

How to pass array of object pointers to function?

I am having trouble passing an array of object pointers from main() to a function from different class.
I created an array of object pointers listPin main() and I want to modify the array with a function editProduct in class Manager such as adding new or edit object.
Furthermore, I want to pass the whole listP array instead of listP[index]. How to achieve this or is there any better way? Sorry, I am very new to c++.
#include <iostream>
using namespace std;
class Product
{
protected:
string id, name;
float price;
public:
Product()
{
id = "";
name = "";
price = 0;
}
Product(string _id, string _name, float _price)
{
id = _id;
name = _name;
price = _price;
}
};
class Manager
{
protected:
string id, pass;
public:
Manager(string _id, string _pass)
{
id = _id;
pass = _pass;
}
string getId() const { return id; }
string getPass() const { return pass; }
void editProduct(/*array of listP*/ )
{
//i can edit array of listP here without copying
}
};
int main()
{
int numProduct = 5;
int numManager = 2;
Product* listP[numProduct];
Manager* listM[numManager] = { new Manager("1","alex"), new Manager("2", "Felix") };
bool exist = false;
int index = 0;
for (int i = 0; i < numProduct; i++) { //initialize to default value
listP[i] = new Product();
}
string ID, PASS;
cin >> ID;
cin >> PASS;
for (int i = 0; i < numManager; i++)
{
if (listM[i]->getId() == ID && listM[i]->getPass() == PASS) {
exist = true;
index = i;
}
}
if (exist == true)
listM[index]->editProduct(/*array of listP */);
return 0;
}
Since the listP is a pointer to an array of Product, you have the following two option to pass it to the function.
The editProduct can be changed to accept the pointer to an array of size N, where N is the size of the passed pointer to the array, which is known at compile time:
template<std::size_t N>
void editProduct(Product* (&listP)[N])
{
// Now the listP can be edited, here without copying
}
or it must accept a pointer to an object, so that it can refer the array
void editProduct(Product** listP)
{
// find the array size for iterating through the elements
}
In above both cases, you will call the function as
listM[index]->editProduct(listP);
That been said, your code has a few issues.
First, the array sizes numProduct and numManager must be compiled time constants, so that you don't end up creating a non-standard variable length array.
Memory leak at the end of main as you have not deleted what you have newed.
Also be aware Why is "using namespace std;" considered bad practice?
You could have simply used std::array, or std::vector depending on where the object should be allocated in memory. By which, you would have avoided all these issues of memory leak as well as pointer syntaxes.
For example, using std::vector, you could do simply
#include <vector>
// in Manager class
void editProduct(std::vector<Product>& listP)
{
// listP.size() for size of the array.
// pass by reference and edit the listP!
}
in main()
// 5 Product objects, and initialize to default value
std::vector<Product> listP(5);
std::vector<Manager> listM{ {"1","alex"}, {"2", "Felix"} };
// ... other codes
for (const Manager& mgr : listM)
{
if (mgr.getId() == ID && mgr.getPass() == PASS)
{
// ... code
}
}
if (exist == true) {
listM[index]->editProduct(listP);
}
You cannot have arrays as parameters in C++, you can only have pointers. Since your array is an array of pointers you can use a double pointer to access the array.
void editProduct(Product** listP){
and
listM[index]->editProduct(listP);
Of course none of these arrays of pointers are necessary. You could simplify your code a lot if you just used regular arrays.
Product listP[numProduct];
Manager listM[numManager] = { Manager("1","alex"), Manager("2", "Felix")};
...
for(int i = 0; i < numManager; i++ ){
if(listM[i].getId() == ID && listM[i].getPass() == PASS) {
exist = true;
index = i;
}
}
if(exist == true){
listM[index].editProduct(listP);
}

Creating a personal string vector class

I am not allowed to make use of the vector class so I need to make my own. I made a int vector class and it works fine, but when trying to make it for strings it compiles but gives me an error because of the pointers. Any hint where I am making the mistake? All I did was change every int element for string, but aparently that does not work. Please help I am very confused.
public:
StringRow(){
elements = new string;
size = 0;
}
~StringRow(){...}
void push_back(string value){...}
};
You defined pointer to variable, not array of variables.
elements = new string;
Replace it with
elements = new string[size];
You can optimize algorithm with defining initial size. Create bigger array only if it's necessary.
There are several problems:
in the constructor you don't need to allocate anything. You don't even need a constructor here, you can initialize the members directly as you declare them.
if you allocate with string* tmpElementsArray = new string[size + 1]; you need to deallocate with delete [] tmpElementsArray;
Corrected working version:
#include <string>
#include <iostream>
using namespace std;
class StringRow {
private:
string* elements = nullptr;
int size = 0;
public:
// constructor not needed
// StringRow() {
// elements = nullptr;
// size = 0;
// }
~StringRow() {
delete []elements;
}
void push_back(string value) {
string* tmpElementsArray = new string[size + 1];
for (int i = 0; i<size; i++) {
tmpElementsArray[i] = elements[i];
}
delete [] elements;
elements = tmpElementsArray;
elements[size] = value;
size++;
}
int length() {
return size;
}
string at(int index) {
if (index<size) {
return elements[index];
}
}
};
int main()
{
StringRow s;
string str1 = "hello";
string str2 = "hello2";
s.push_back(str1);
s.push_back(str2);
cout << s.at(0) << endl ;
cout << s.at(1) << endl;
}
Doing a delete []elements if elements is nullptr is OK.
NB: This is not the most efficient way.

Counting number of occurrences of a string in a Hash Table

I am writing my own HashTable class in C++ and need to output to the user the number of occurrences of each string in the table. For example, if this is the input: testing, 1, 2, testing, and this is the hash table (done with chaining, and node pointers):
[0]->testing, testing
[1]->2
[2]->1
this would be the output to the user (the count, followed by the word):
2 testing
1 2
1 1
The problem I'm having is how to keep track of how many of each word is in the Hash Table, or how to find it. I started with this question but was unable to implement another array in my code.
I also tried the solution in this question, but it didn't work because of my use of pointers/chained hashing.
My question is, do I need to use a separate array of strings to keep track of what's already been used, or is there an easy way to recursively go through each index of the Hash Table and print out the number of occurrences of each string? I think I need to accomplish this in either my insert function or my printData function.
For reference, here is my code:
HashTable.h:
#include <string>
#include <iostream>
using namespace std;
struct Entry {
string word;
Entry* next;
};
class HashTable {
public:
HashTable();
HashTable(int);
int hash(string);
void insert(string);
void printData();
int getCapacity() const;
private:
//Member variables
int CAPACITY; // The initial capacity of the HashTable
Entry **data; // The array to store the data of strings (Entries)
};
HashTable.cpp:
#include "HashTable.h"
HashTable::HashTable()
{
CAPACITY = 0;
data = new Entry*[0];
}
HashTable::HashTable(int _cap)
{
CAPACITY = _cap;
data = new Entry*[_cap];
for (int i = 0; i < CAPACITY; i++) {
data[i] = new Entry;
data[i]->word = "empty";
data[i]->next = nullptr;
}
}
int HashTable::hash(string key)
{
int hash = 0;
for (unsigned int i = 0; i < key.length(); i++) {
hash = hash + (int)key[i];
}
return hash % CAPACITY;
}
void HashTable::insert(string entry)
{
int index = hash(entry);
if (data[index]->word == "empty") {
data[index]->word = entry;
} else {
Entry* temp = data[index];
Entry* e = new Entry;
e->word = entry;
e->next = nullptr;
while (temp->next != nullptr) {
temp = temp->next;
}
temp->next = e;
}
}
void HashTable::printData()
{
for (int i = 0; i < CAPACITY; i++) {
if (data[i]->next != nullptr) {
while(data[i]->next != nullptr) {
cout << data[i]->word << " -> ";
data[i] = data[i]->next;
}
cout << data[i]->word << endl;
} else {
cout << data[i]->word << endl;
}
}
}
int HashTable::getCapacity() const
{
return CAPACITY;
}
NOTE: I can't use any function/data structure from the standard C++ Library.
I only see two options here
Traverse entire linked list to count occurances. Use a map< string, int > to count occurances for each string.
You should make your linked list sorted. So when you insert a new node, you will insert it in its exact place. You can use strcmp for comparison. This way you can count every word exactly in one traverse and using just one integer variable, but your insert time and complexity will increase.

Getline() and cin manipulate dynamic array

I'm totally lost and confused and could use some help.
I'm currently working on a small command line-based game. For this I wrote a class Inventory, dynamically creating an array of invSpace-objects, each space representing a pair of a pointer to an Item (another class of mine) and a integer, depicting a quantity. Here's the code:
class invSpace {
public:
Item *item;
int quantity;
invSpace() {
item = NULL;
quantity = 0;
}
};
class Inventory {
private:
invSpace* spaces = NULL;
size_t size;
public:
int free_space() {
int free = 0;
for (int i = 0; i < size; i++) {
if (spaces[i].item == NULL) {
free++;
}
}
return free;
}
Inventory() {}
Inventory(size_t new_size) {
size = new_size;
spaces = new invSpace[size];
for (int i = 0; i < size; i++) { //I know this is obsolete because
spaces[i].item = NULL; //of the invSpace constructor, I
spaces[i].quantity = 0; //just did this for testing
}
~Inventory() {
delete[] spaces;
}
invSpace& operator[](int index) {
return spaces[index];
}
};
There are some more methods in this class, like for adding, deleting and searching for items, but those don't matter now. So this is basically just a simple array within one object, dynamically allocating memory in the constructor and with some extra methods. After being created, the array contains zero elements, or Items, so the free_space() method should return the size of the array. But it doesn't. It returns about half of the size.
My first thought was that something went wrong with the allocation. But at a second glance I noticed that the Inventory is totally fine directly after being created; with exactly as many spaces as requested, all of them set to item=NULL/quantity=0. But after a call of getline() at the start of main() that scans user input and saves it to a string for further analyzing, some spaces get filled with random addresses and integers.
Even stranger, with each new call of getline() some spaces are freed, some others filled. As far as my debugging, experimenting and testing goes, none of these addresses belong to any variable in my program, they are just plain random. Also, at no point is there be any interference with the Inventory and the getline() function or the string it returns. In fact, after being created, no part of this object is used anywhere in the code beside the free_space() method. What's even stranger is that spaces in the Inventory class is marked private, so a method is required to meddle with this pointer/array (or so I would expect).
This problem occurs with getline() and cin but not with any of C's <stdio.h> input stream functions. Using malloc() instead of new[] makes no difference. Of course, I could use something like scanf() for the reading from the console. Still, I just want to know why all these things happen. I have absolutely no idea.
Thanks in advance for every answer!
EDIT:
I narrowed the whole code so that it still produces the same error, also changed free_space() so that it prints adress and integer if present:
#include <iostream>
#include <string>
#include <map>
using namespace std;
class Item {
public:
static map<string, Item*> itemlist;
string name;
string description;
Item() {}
Item(const string new_name, const string new_description) {
name = new_name;
description = new_description;
itemlist.insert(pair<string, Item*> (name, this));
}
};
map<string, Item*> Item::itemlist;
/*The more Items are declared, the more random adresses appear in the
inventory*/
Item item01("sword", "A sharp and deadly weapon.");
Item item02("shield", "This will protect you. To a certain extent.");
Item item03("stick", "What is this for exactly?");
Item item04("bottle of water", "A bottle full of refreshing spring water.");
class invSpace {
public:
Item *item;
int quantity;
invSpace() {
item = NULL;
quantity = 0;
}
};
class Inventory {
private:
invSpace* spaces = NULL;
size_t size;
public:
int free_space() {
int free = 0;
for (int i = 0; i < size; i++) {
if (spaces[i].item == NULL) {
free++;
cout << i << " = free" << endl;
}
else {
cout << spaces[i].item << " / " << spaces[i].quantity << endl;
}
}
return free;
}
Inventory() {}
Inventory(size_t new_size) {
size = new_size;
spaces = new invSpace[size];
for (int i = 0; i < size; i++) {
spaces[i].item = NULL;
spaces[i].quantity = 0;
}
}
~Inventory() {
delete[] spaces;
}
};
class Player {
public:
string name;
Inventory inventory;
Player(const string new_name) {
inventory = Inventory(40);
name = new_name;
}
};
Player player("Me");
int main() {
string input;
//Inventory inventory(40); //no error when declared outside the Player class
while (1) {
cout << "\n>> ";
getline(cin, input);
if (input == "x") {
break;
}
else {
player.inventory.free_space();
}
}
}
Some things I noticed: No error occurs if the inventory isn't part of a Player-object. If it is but no Items are declared only the first inventory space receives a random adress (and int value) after the first call of getline().
The more Items there are, the more random adresses I get, it seems...

Dynamic Memory Allocation for Dictionary

Hi there I need to Build something like a dictionary and each word according to my code can have 100 meanings, but maybe it has only 5 meanings then I will be allocating 95 extra space for nothing or maybe it has more than 100 meanings then the program will crash, I know the vector class is very easy and could be good use of, but the task is almost building my own vector class, to learn how it works. Thus **meanings and some other stuff remain the same and here is my code, Also I know I am causing memory leakage, how can I delete properly? :
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class Expression {
char *word_with_several_meanings; // like "bank", "class"
char **meanings; // a pointer to a pointer stores all meanings
int meanings_ctr; // meanings counter
//-----------FUNCTIONS------------------------------------------------
public:
void word( char* = NULL );
void add_meaning(char* = NULL);
char* get_word();
int get_total_number_of_meanings();
char* get_meaning(int meanx = 0);
Expression(int mctr = 0); // CTOR
~Expression(); // DTOR
};
Expression::Expression(int mctr ) {
meanings_ctr = mctr; // Setting the counter to 0
meanings = new char * [100]; // Allocate Space for 100 meanings
}
Expression::~Expression() {
delete [] meanings; // Deleting the memory we allocated
delete [] word_with_several_meanings; // Deleting the memory we allocated
}
void Expression::word( char *p2c )
{
word_with_several_meanings = new char[strlen(p2c)+1];
// copy the string, DEEP copy
strcpy(word_with_several_meanings, p2c);
}
void Expression::add_meaning(char *p2c)
{
//meanings = new char * [meanings_ctr+1];
meanings[meanings_ctr] = new char[strlen(p2c)+1];
strcpy(meanings[meanings_ctr++],p2c);
}
char * Expression::get_meaning( int meanx )
{
return *(meanings+meanx);
}
char * Expression::get_word()
{
return word_with_several_meanings;
}
int Expression::get_total_number_of_meanings()
{
return meanings_ctr;
}
int main(void) {
int i;
Expression expr;
expr.word("bank ");
expr.add_meaning("a place to get money from");
expr.add_meaning("b place to sit");
expr.add_meaning("4 letter word");
expr.add_meaning("Test meaning");
cout << expr.get_word() << endl;
for(int i = 0; i<expr.get_total_number_of_meanings(); i++)
cout << " " << expr.get_meaning(i) << endl;
Expression expr2;
expr2.word("class");
expr2.add_meaning("a school class");
expr2.add_meaning("a classification for a hotel");
expr2.add_meaning("Starts with C");
cout << expr2.get_word() << endl;
for( i = 0; i<expr2.get_total_number_of_meanings(); i++)
cout << " " << expr2.get_meaning(i) << endl;
Expression expr3;
expr3.word("A long test ... ");
char str[] = "Meaning_ ";
for (int kx=0;kx<26;kx++)
{
str[8] = (char) ('A'+kx);
expr3.add_meaning(str);
}
cout << expr3.get_word() << endl;
for(i = 0; i < expr3.get_total_number_of_meanings(); i++)
cout << " " << expr3.get_meaning(i) << endl;
return 0;
}
When you are allocating a multi dimensional array with new then you are allocating it with a loop, e.g.
char **x = new char*[size]
for (int i = 0; i < N; i++) {
x[i] = new int[size];
}
So you also have to delete it in this fashion:
for (int i = 0; i < N; i++) {
delete[] x[i];
}
delete[] x;
Thus when you're having arbitrary sizes of your array you'll have to store them somewhere for using them within the destructor.
delete [] meanings; // Deleting the memory we allocated
won't get rid of your memory allocated, only the pointers themselves.
To free up the actual memory, you will need to iterate through your meanings array, and delete [] each element in it.
Something like:
for (int i = 0; i < meanings_ctr; ++i)
{
delete [] meanings[meanings_ctr];
meanings[meanings_ctr] = NULL;
}
delete [] meanings;
--
For the problem of what to do if you get more than 100 meanings (or in general when your collection is full), the standard technique is to allocate a new array that is double the size (which you can do since it is dynamic), copy your existing collection into that one, and then dispose of your existing one.
I'd use a simple linked list (this is simplified, not complete and untested; also there should be proper getters/setters and stuff):
class Meaning {
char text[20];
Meaning *next;
Meaning(const char *text) : next(0) {
strcpy(this->text, text);
}
}
class Word {
char text[20];
Meaning *first;
Meaning *last;
Word(const char *text) : first(0), last(0) {
strcpy(this->text, text);
}
~Word() {
Meaning *m = first, *n;
while(m) {
n = m->next;
delete m;
m = n;
}
}
void AddMeaning(const char *text) {
if (last) {
last = last->next = new Meaning(text);
}
else {
first = last = new Meaning(text);
}
}
void print() {
printf("%s:\n\t", text);
Meaning *m = first;
while (m) {
printf("%s, ", m->text);
m = m->next;
}
}
}