Expand function isn't properly expanding for my Graph - c++

Does anyone see something overtly wrong with my expand function below? I've included the private section of the class and my vertex_node struct to give some context. I'm not sure why it isn't expanding properly. Any help would be appreciated.
private:
//list is pointers to vertex nodes;
struct vertex_node {
string name;
set <string> edges;
};
vertex_node **list;
void Graph:: expand()
{
int new_cap = capacity * 2+1;
//creates new larger array
vertex_node **larger_array = new vertex_node*[new_cap];
//loop through all elements of old array
for(int i = 0; i<capacity; i++){
if(list[i] != NULL){
//rehash each element and place it in new array
int a = hash_string(list[i]->name) % new_cap;
larger_array[a] = new vertex_node;
larger_array[a]->name = list[i] -> name;
larger_array[a]->edges = list[i] -> edges;
}
//delete old list
delete[] list;
list = larger_array;
capacity = new_cap;
}
}

as I mentioned in my comment above you're invalidating the whole array at the end of the 1st iteration. Your attempt at avoiding a memory leak is commendable but it has to be done in 2 places.
for(int i = 0; i<capacity; i++){
if(list[i] != NULL){
//rehash each element and place it in new array
int a = hash_string(list[i]->name) % new_cap;
larger_array[a] = new vertex_node;
larger_array[a]->name = list[i] -> name;
larger_array[a]->edges = list[i] -> edges;
}
//clean up every memory location once you're done with it
delete list[i];
list = larger_array;
capacity = new_cap;
}
//clean the whole array at the very end
delete[] list;

Related

Hash table - issue with destructor (pointer being freed was not allocated)

I have a HashTable, where collisions are handled by chaining (linked lists). The first node of every linked list has a pointer from each array position. Shown below is a regular constructor along with rule of 3 functions.
Although my code is compiling and my functions (add, remove, etc) are producing the right output, I am having an issue with the destructor (the IDE points to it with a Thread 1: signal SIGABRT) and the console displays "pointer being freed was not allocated" after my driver program finishes running. I can't figure out what went wrong so any help would be appreciated. I did not include my code for any of the other functions (add, remove, etc) aside from constructors/destructors.
Even when I comment out the copy and overloaded= constructors, the same issue still arise with the destructor.
specification:
class HashTable {
public:
HashTable(int);
~HashTable();
HashTable(const HashTable &);
HashTable& operator=(const HashTable &);
private:
struct Node {
string word;
int wordCount;
Node * next;
// node constructor
Node(string w, int count) {
word = w;
wordCount = count;
next = nullptr;
}
};
Node** wordList;
int capacity;
int hashFunction(string);
};
Implementation of big 4:
constructor:
HashTable::HashTable(int cap) {
capacity = cap;
wordList = new Node*[capacity];
for (int i = 0; i < capacity; i++)
wordList[i] = nullptr;
}
destructor (where the problem seems to be)
HashTable::~HashTable() {
for (int i = 0; i < capacity; i++) {
Node* curr = wordList[i];
while (curr != nullptr) {
Node* prev = curr;
curr = curr->next;
delete prev;
}
}
delete[] wordList;
}
copy constructor:
HashTable::HashTable(const HashTable &obj) {
capacity = obj.capacity;
wordList = new Node*[capacity];
for (int i = 0; i < capacity; i++) {
if (obj.wordList[i] == nullptr)
continue;
Node * newNode = new Node(obj.wordList[i]->word,
obj.wordList[i]->wordCount);
wordList[i] = newNode;
}
}
copy assignment operator:
HashTable& HashTable::operator=(const HashTable &obj) {
if (this != &obj) {
for (int i = 0; i < capacity; i++) {
Node* curr = wordList[i];
while (curr != nullptr) {
Node* prev = curr;
curr = curr->next;
delete prev;
}
}
delete[] this->wordList;
this->capacity = obj.capacity;
this->wordList = new Node*[capacity];
for (int i = 0; i < this->capacity; i++) {
if (obj.wordList[i] == nullptr)
continue;
Node * newNode = new Node(obj.wordList[i]->word,
obj.wordList[i]->wordCount);
this->wordList[i] = newNode;
}
}
return *this;
}
In your copy constructor and copy assignment operator, you are copying the list pointers from obj into this. This leaves the same pointers in both objects, resulting in double free and other issues once one HashTable has been freed,
When you do the copies, you need to do a Deep Copy, which is to allocate new nodes for the copy of the word list.

Segmentation fault caused by deleting pointer that should be null

I have written a custom string and vector classes. Occasionally (but consistently) when adding the string to the vector, the program tries to delete the char pointer (which should be null), causing a seg fault. Even going step by step through the debugger, I cannot figure out how this pointer is not a nullptr as it normally is.
Here are my three constructors for my string class, as well as the = overload which is where the segfault is occurring. Note, I do have a simple destructor that deletes both data and tempreturn if they are not null.
//default constructor
AString::AString(){
this->length = 0;
this->cap = 0;
this->data = nullptr;
this->tempReturn = nullptr;
}
//constructor with arguments for char*
AString::AString(const char* newData){
this->length = strlen(newData);
this->cap = this->length + 1;
this->data = new char[this->cap];
this->tempReturn = nullptr;
for(int i = 0; i < this->length; i ++){
this->data[i] = newData[i];
}
this->data[length] ='\0';
}
//constructor with arguments for passing a string by reference
AString::AString(const AString& newData){
this->length = newData.length;
this->cap = this->length + 1;
this->data = new char[this->cap];
this->tempReturn = nullptr;
for(int i = 0; i < this->length; i++){
this->data[i] = newData.data[i];
}
this->data[length] ='\0';
}
//called with the = string reference
AString& AString::operator= (const AString& newData){
if(data != nullptr){
delete[] data; //This is where it seg faults.
}
this->length = newData.length;
this->cap = newData.length + 1;
this->data = new char[this->cap];
for(int i = 0; i < this->length; i ++){
data[i] = newData.data[i];
}
this->data[length] ='\0';
return *this;
}
At that line, data should be a nullptr and length should be zero. However, at the same element each time, data is filled with a location that holds no chars and length is 33. tempreturn, which should also be null, appears to be holding nonsensical chars
Also for reference, my vector constructor and pushback method that leads into the seg fault
//default constructor setting number of elements to empty, and give a
//default capacity and initialize array;
template <class T>
AVector<T>::AVector(){
this->cap = 10;
this->numElements = 0;
this->data = new T[cap];
}
template <class T>
void AVector<T>::pushBack(T type){
//add it to the end of the current index
this->data[numElements] = type; //line that leads into the segfault
//increase the tracker for how many elements added
numElements++;
//if new element exceeds previously established array
if(numElements >= cap) {
increaseCapacity(this->cap * 2);
}
}

Create an dynamic array in class member function

I am trying to make a dynamic array in my member function, however, it seems to create a new dynamic array each time I call the function. Is there anyway to create a dynamic array inside a member function so it doesn't remake itself.
class predator
{
private:
string name;
string species;
protected:
string *list;
public:
predator(string theSpecies);
void killsRecorded(string kills); // add a new kill to the end of the predator's list of kills
string *killsList(); // return a pointer to the array of all kills by this predator
int noOfTotalKills(); // how many kills have been recorded
int k;
static int n;
};
//The header file
void predator::killsRecorded(string kills)
{
k = 0;
list = new string[5];
*(list + k) = kills;
k = n++;
cout<< k<< endl;
}
string* predator::killsList()
{
//cout<< (sizeof(list)/sizeof(list[0]))<< endl;
for(int i=0; i<5; i++)
{
cout<< *(list + i)<< endl;
}
}
Above is my class and header file, void killsRecorded(string kills) should add kills to my array, however, when I try that in my main.
predator *prey;
prey = new predator("Cheetah");
prey->killsRecorded("Mouse");
prey->KillsRecorded("Donkey");
prey->killsList();
It prints out
Created a hunter that is a Cheetah
0
1
Donkey
*BLANK LINE
*BLANK LINE
*BLANK LINE
*BLANK LINE
Instead, Mouse should be in the first line and Donkey in the second. Am I doing something wrong? Also, I can't use vectors, it's for an assignment.
In your constructor, assign n a default value, say 5. Then create an array of that size.
predator::predator()
: n(5),
k(0)
{
kills = new string[n];
}
Then recordKills checks to see if there is space in kills, reallocating if necessary:
recordKills(string kill)
{
if(k >= n) {
string* oldKills = kills;
kills = new string[2*n];
// copy
for(int i = 0; i< n: i++) {
kills[i] = oldKills[i];
}
n *= 2;
delete [] oldKills;
}
kills[k++] = kill;
}
It's generally a bad idea to call a variable by the name of a data structure, so I renamed 'list' to 'kills'.
Then when printing the kills, loop until k:
string* listKills()
{
for(int i = 0; i < k; i++) {
cout << kills[i] << endl;
}
return kills;
}
Remember to delete kills in the destructor!
Hmm, your killsRecorded(string kills) method is an example of how not to program...
you erase list losing all previously recorded kill
you lose the pointer obtained by a previous new[] which leads to a memory leak (how could you free them now your program has forgotten what had been allocated)
What should be done (what vector class does under the hood):
define a chunk of slots that you initially allocate
add the recorded strings to this simple array until it is full
when it is full allocate another array say of twice the size, carefully copy the values from the old array, release the old array and only them affect the new array to the saved pointer
do not forget to release the allocated array in class destructor
and store in the class the current size (number of kills) and the maximum size (allocated size)
Code could be:
class predator
{
private:
string name;
string species;
protected:
string *list;
size_t max_size;
size_t cur_size;
public:
predator(string theSpecies);
void killsRecorded(string kills); // add a new kill to the end of the predator's list of kills
string *killsList(); // return a pointer to the array of all kills by this predator
int noOfTotalKills(); // how many kills have been recorded
/*int k; what it that???
static int n;*/
};
//The implementation file
predator(string theSpecies): species(species) {
list = new string[5];
max_size = 5;
cur_size = 0;
// what do you do with name ?
}
void predator::killsRecorded(string kills)
{
if (cur_size >= max_size) { /* need a bigger array */
max_size *= 2;
temp = new string[max_size];
for(int i=0; i<cursize; i++) { // copy previous recorded values
temp[i] = list[i];
}
delete[] list; // free previous allocated array
list = temp; // ok list is now big enough
}
list[cur_size++] = kills;
}
You should use std::vector...
to do that you have to
#include <vector>
with the command
std::vector<string> kills;
you can create a new vector of strings
with the command
kills.pushback(stringvalue);
you can add a new string into your vector "list" also you don't have to count your kills... you can use
kills.size();
to get the number of strings back.
To get the values (strings) back you can use the vector like an array
string name = kills[3];
btw: you should save the vector as a member... to do that you have to save it in your class definition (header)
If you arn't allowed to use std::vector, you can write your own list...
class list
{
private:
node* head;
int size = 0;
struct node
{
node* next;
string value;
}
public:
list();
~list();
void PushBack(string);
string GetElement(int index);
int GetSize();
};
list::list()
{
head = new list();
head->next = nullptr;
}
list::~list()
{
node* temp = head;
node* temp2 = temp;
do //delete hole list
{
temp2 = temp->next;
delete temp;
temp = temp2;
}while(temp != nullptr);
}
void list::PushBack(string item)
{
node* temp = head;
while(temp->next != nullptr)
{
temp = temp->next;
}
//found the end of the list
node* newNode = new node();
newNode->value = item;
newNode->next = nullptr;
temp->next = newNode;
size++;
}
int list::GetSize()
{
return size;
}
string list::GetElement(int index)
{
node* temp = head;
while(temp->next != nullptr)
{
temp = temp->next;
if(index == 0)
{
return temp->value;
}
index--;
}
//index out of bounds
return "";
}
I can not check if the code is correct at the moment, because on this computer is no IDE... but I think it should word ;)
BTW: you can use this list instead of an array to do that you have to write:
list kills;
kills.PushBack("Peter");
kills.PushBack("Thomas");
kills.PushBack("Alex");
for(int i = 0; i< kills.GetSize();i++)
{
std::cout<<kills.GetElement(i)<<std::endl;
}

Creating objects dynamically c++

Well, i've read many different posts about this topic, but none could solve my question.
How can i dynamically create objects, and store them in a linked list.
i've this code that an object saves a number, and then it has a pointer that points to the next number, for representation only.
For example: 17
One->next = seven. Boths are objects of the same class.
class Class{
private:
int value;
Class *pNext; //Points to the next object in the linked list.
public:
Class(){value = 0; }
~Class(){;}
void setV(int x){ value = x;}
int getV(){return value;}
//void setP(Class *p){ pNext = p;} ?? Is this right?
};
int main(){
Class *pFirst; //pointer to first element
Class *pLast; //pointer to last element
Class *pCurrent; //pointer to current element
for(int i = 0; i < 4;i++){
pCurrent = new Class;
pCurrent->setV(i);
//pCurrent->setP(NULL);
}
for(int i = 0; i < 4;i++){
cout << pCurrent->getV() << " ";
}
return 0;
}
Thanks
To make the list permanent, you first have to declare your head node and then for each iteration in your for loop, add on to that list.
int main()
{
Class *pFirst; //pointer to first element
pFirst->setV(0);
Class *pLast; //pointer to last element
Class *pCurrent; //pointer to current element
pFirst->pNext = pCurrent; // To keep track of the list via head
for(int i = 0; i < 4;i++)
{
pCurrent = new Class;
pCurrent->setV(i);
pCurrent->pNext = NULL;
}
for(int i = 0; i < 4;i++)
{
cout << pCurrent->getV() << " ";
}
return 0;
}

reHashing a table

I am trying to rehash a table by deleting old table and creating a new bigger table with same contents. I created a reHash function, but this function gives memory leaks, causing the program to crash when the function is executed. I can't find my error.
void HashMap::reHash()
{
int OldCapacity = cap;
cap = cap * 2 + 1; //set new capacity
Node** newHashTable = new Node*[cap]; //create a temporary table to hold info
for (int i = 0; i < cap; i++)
{
newHashTable[i] = nullptr;
}
const Node* n;
//fill in the new temp table with old info
for (int i = 0; i < OldCapacity; ++i)
{
n = HashTable[i];
while (n != nullptr)
{
//initialize new node
Node* nod = new Node;
nod->key = n->key;
nod->value = n->value;
nod->next = nullptr;
Node*& bucket = newHashTable[default_hash_function(n->key)/cap];
nod->next = bucket;
bucket = nod;
n = n->next;
}
}
// delete the links
for (int i = 0; i < OldCapacity; ++i)
{
Node *curr = HashTable[i];
Node *next;
while (curr != nullptr)
{
next = curr->next;
delete curr;
curr = next;
}
}
HashTable = newHashTable;
}
Your fundamental leak is this:
HashTable = newHashTable;
You never deleted the old pointer-array. It should be this:
delete [] HashTable;
HashTable = newHashTable;
You also aren't computing the modulo of your hash function correctly for your table size in the reassignment, which would be devastating to your hash table.
This:
Node*& bucket = newHashTable[default_hash_function(tmp->key) / cap];
should be this:
Node*& bucket = newHashTable[default_hash_function(tmp->key) % cap];
// note modulo operator -------------------------------------^
Rehash Sans-Allocations
Honestly, none of the dynamic allocations are needed in this except allocating the new bed. You can use the existing nodes by moving them from the old bed to the new one.
void HashMap::reHash()
{
int OldCapacity = cap;
cap = cap * 2 + 1;
// allocate new bed. note: uses () to value-initialize nullptr entries
Node** newHashTable = new Node*[cap]();
//fill in the new temp table with old info
for (int i = 0; i < OldCapacity; ++i)
{
Node *n = HashTable[i];
while (n != nullptr)
{
// advance n *before* moving node to new hash bed
Node *tmp = n;
n = n->next;
// find the proper collision list pointer.
Node*& bucket = newHashTable[default_hash_function(tmp->key) % cap];
tmp->next = bucket;
bucket = tmp;
}
}
delete [] HashTable;
HashTable = newHashTable;
}