I'm just a humble student looking to further my knowledge about the C++ language. My professor isn't helping! I think the title along with comments in-code explain my issues clearly.
#ifndef H_Htable
#define H_Htable
//****************************************************************
// Author: D.S. Malik
//
// This class specifies the members to implement a hash table as
// an ADT. It uses quadratic probing to resolve collisions.
//****************************************************************
#include <iostream>
#include <cassert>
using namespace std;
template <class elemType>
class hashT
{
public:
void insert(int hashIndex, const elemType& rec);
//Function to insert an item in the hash table. The first
//parameter specifies the initial hash index of the item to
//be inserted. The item to be inserted is specified by the
//parameter rec.
//Postcondition: If an empty position is found in the hash
// table, rec is inserted and the length is incremented by
// one; otherwise, an appropriate error message is
// displayed.
//sequential search
bool search(int& hashIndex, const elemType& rec, bool found = false) const;
//Function to determine whether the item specified by the
//parameter rec is in the hash table. The parameter hashIndex
//specifies the initial hash index of rec.
//Postcondition: If rec is found, found is set to true and
// hashIndex specifies the position where rec is found;
// otherwise, found is set to false.
bool isItemAtEqual(int hashIndex, const elemType& rec) const;
//Function to determine whether the item specified by the
//parameter rec is the same as the item in the hash table
//at position hashIndex.
//Postcondition: Returns true if HTable[hashIndex] == rec;
// otherwise, returns false.
void retrieve(int hashIndex, elemType& rec) const;
//Function to retrieve the item at position hashIndex.
//Postcondition: If the table has an item at position
// hashIndex, it is copied into rec.
void remove(int hashIndex, const elemType& rec);
//Function to remove an item from the hash table.
//Postcondition: Given the initial hashIndex, if rec is found
// in the table it is removed; otherwise, an appropriate
// error message is displayed.
void print() const;
//Function to output the data.
//provide for both int and string data types in the hash table
hashT(int size = 101, bool isIntTable = true);
//constructor
//Postcondition: Create the arrays HTTable and indexStatusList;
// initialize the array indexStatusList to 0; length = 0;
// HTSize = size; and the default array size is 101.
~hashT();
//destructor
//Postcondition: Array HTable and indexStatusList are deleted.
private:
elemType *HTable; //pointer to the hash table
int *indexStatusList; //pointer to the array indicating the
//status of a position in the hash table
int length; //number of items in the hash table
int HTSize; //maximum size of the hash table
};
template <class elemType>
void hashT<elemType>::insert(int hashIndex, const elemType& rec)
{
int pCount;
int inc;
pCount = 0;
inc = 1;
while (indexStatusList[hashIndex] == 1
&& HTable[hashIndex] != rec
&& pCount < HTSize / 2)
{
pCount++;
hashIndex = (hashIndex + inc) % HTSize;
inc = inc + 2;
}
if (indexStatusList[hashIndex] != 1)
{
HTable[hashIndex] = rec;
indexStatusList[hashIndex] = 1;
length++;
}
else
if (HTable[hashIndex] == rec)
cerr << "Error: No duplicates are allowed." << endl;
else
cerr << "Error: The table is full. "
<< "Unable to resolve the collision." << endl;
}
//sequential search
template <class elemType>
bool hashT<elemType>::search(int& hashIndex, const elemType& rec, bool found) const
{
for (int i = 0; i < HTSize; i++) {
if (HTable[i] == rec) { //assuming no repeat data
found = true;
hashIndex = i;
break;
}
}
return found;
}
template <class elemType>
bool hashT<elemType>::isItemAtEqual(int hashIndex, const elemType& rec) const
{
//first make sure the item has not been removed
if (indexStatusList[hashIndex] != -1) {
//make equality comparison
if (HTable[hashIndex] == rec)
return true;
else
return false; //comparison fails
}
else
{
std::cerr << "isItemEqual(): Item has been removed" << endl;
return false;
}
}
template <class elemType>
void hashT<elemType>::retrieve(int hashIndex, elemType& rec) const
{
if (indexStatusList[hashIndex] != -1)
rec = HTable[hashIndex];
else
std::cerr << "retrieve(): item has been removed" << endl;
}
template <class elemType>
void hashT<elemType>::remove(int hashIndex, const elemType& rec)
{
//make sure the item hasn't already been removed
if (indexStatusList[hashIndex] != -1) {
bool isInList = hashT<elemType>::search(hashIndex, rec);
//update the status
if (isInList)
{
indexStatusList[hashIndex] = -1;
length--; //decrement length
}
else
std::cerr << "hasT::remove() could not remove the specified item" << endl;
}
else
{
std::cerr << "remove(): Item has already been removed from the table" << endl;
}
}
template <class elemType>
void hashT<elemType>::print() const
{
std::cout << "Hash Table Data: " << endl;
for (int i = 0; i < (length - 5); i++) {
elemType item = HTable[i];
//std::cout << item << " ";
}
}
template <class elemType>
hashT<elemType>::hashT(int size, bool isIntTable)
{
HTable = new elemType[]; //is this right? HTable is an array just like indexStatusList
HTSize = size;
length = 0;
indexStatusList = new int[0]; //I think this one works?
}
template <class elemType>
hashT<elemType>::~hashT() //deleting always causes heap errors!!!
//says writing to unallocated memory -- debugging shows otherwise
{
//delete[] HTable;
//delete[] indexStatusList; //still causing errors -- error now not associated with any particular line (of my code)
}
#endif
I've kept increasing my bounds checking security when instantiating hashT in main. I'm convinced it is because my data members are being initialized incorrectly. This is one error message I get after trying a few things: "Unhandled exception at 0x773F627C (ntdll.dll) in exercise7Chap9.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77426480)."
finally, here's main just in case:
#include <iostream>
#include "hashT.h"
int main() {
//add one item and test for equality
//hashT<int> ht = hashT<int>(20);
//ht.insert(0, 1);
//bool itemInsertSuccess = ht.isItemAtEqual(0, 1);
//if (itemInsertSuccess)
// std::cout << "first test has succeeded" << endl;
//else
// std::cout << "first test has failed" << endl;
////remove item and make sure isItemEqual returns false
//ht.remove(0, 1);
//bool itemRemoved = ht.isItemAtEqual(0, 1);
//if (!itemRemoved)
// std::cout << "second test passed" << endl;
//else
// std::cout << "second test failed" << endl;
//add many items then make sure search() works
hashT<int> ht1 = hashT<int>(51);
for (int i = 0; i < 10; i++)
ht1.insert(i, i);
int indx = -1;
ht1.search(indx, 0);
if (indx == 25)
std::cout << "Test 3 has passed" << endl;
else
std::cout << "Test 3 has failed" << endl;
//print data then test retrieve() and print a single item
/*ht1.print();
int item = -1;
ht1.retrieve(10, item);
if (item != -1) {
std::cout << item << endl;
std::cout << "test 4 has passed" << endl;
}
else
std::cout << "test 4 has failed" << endl;
hashT<int> HtRetrieve = hashT<int>(10);
HtRetrieve.insert(0, 0);
int it = -1;
HtRetrieve.retrieve(0, it);
std::cout << it << endl;*/
char stop;
std::cin >> stop;
//return 0;
}
In your case here, scrap the variable isIntTable. Templates are a compile-time construct and run-time values won't influence how the template is compiled into a class in any way.
Then, in your constructor, just use the template type as the one you are allocating.
template <class elemType>
hashT<elemType>::hashT(int size)
{
HTable = new elemType[size];
length = 0;
indexStatusList = new int[0];
}
However, this could be much better. Consider using initialisation instead of assignation:
hashT<elemType>::hashT(int size) :
HTable{new elemType[size]},
length{size},
indexStatusList{int[size]} { /* empty constructor */ }
And it can be even better. Consider using smart pointer instead of raw owning pointers and vectors instead of dynamic allocated array:
template<typename T>
struct hashT {
// using the right type for size
hashT(std::size_t size) : pointerToOneT{std::make_unique<T>()}, HTable(size) {}
// make_unique is the `new` for unique pointers
// no need for destructors, the vector and unique_ptr are freeing themselves
private:
std::unique_ptr<T> pointerToOneT;
std::vector<T> HTable;
std::vector<int> indexStatusList;
};
If you don't want to use std::vector, you can always use std::unique_ptr<T[]>, which is a dynamically allocated array that free itself.
template<typename T>
struct hashT {
// using the right type for size
hashT(std::size_t size) :
HTable{std::make_unique<T[]>(size)},
indexStatusList(std::make_unique<int[]>(size)) {}
private:
std::unique_ptr<T[]> HTable;
std::unique_ptr<int[]> indexStatusList;
};
EDIT
In your actual destructor, the problem is that you initialized the int* with the new[] but you are using delete. To delete an array, you must use delete[]
template <class elemType>
hashT<elemType>::~hashT()
{
delete HTable;
delete[] indexStatusList;
}
Turns out this heap corruption was caused by my lack of understanding dynamic arrays in C++.
My incorrect initialization of the arrays HTable and indexStatusList were: Htable = new elemType();, HTable = new elemType[]; and indexStatusList = new int[0];
I simply needed to add the size as an argument (never seen a size argument passed in brackets before!)
Here is the working constructor:
//constructor
template <class elemType>
hashT<elemType>::hashT(int size)
{
HTable = new elemType[size]; // pass size so the compiler knows what to allocate and deallocate
HTSize = size;
length = 0;
indexStatusList = new int[size];
}
working destructor:
template <class elemType>
hashT<elemType>::~hashT()
{
delete[] HTable;
delete[] indexStatusList;
}
Related
I am working on a project which I have to include a header file to my main.cpp. The header file is a heap which is using a template file. For reasons that escape me the insert and remove functions cannot be "seen" in the main file. I am getting an error message: C:/Users/Tito/Documents/C++proj/cs3304/Homework2_2/Homework10/main.cpp:58:17: error: request for member 'remove' in 'enter1', which is of non-class type 'priority_queue_heap()'. Can someone please tell me where I am going wrong? I will really appreciate it.
Thanks
Here are the lines of code:
Main.cpp:
/**
* Insert a few elements into a heap and the remove them
* one by one and see if we get them in the right.
*/
#include "priority_queue_heap.h"
#include "heap.h"
#include <iostream>
#include <ctime>
using namespace std;
int test1() {
heap<int> hp;
hp.insert(1);
hp.insert(2);
hp.insert(3);
hp.insert(4);
hp.insert(5);
hp.check_heap();
int x = hp.remove();
cout << "removed " << x << endl;
x = hp.remove();
cout << "removed " << x << endl;
x = hp.remove();
cout << "removed " << x << endl;
x = hp.remove();
cout << "removed " << x << endl;
x = hp.remove();
cout << "removed " << x << endl;
cout << "empty? " << hp.is_empty() << endl;
}
void test2() {
srand(time(NULL));
heap<int> hp;
for(int i = 0; i < 30; i++ ) {
hp.insert(rand());
}
while(!hp.is_empty()) {
int x = hp.remove();
cout << x << endl;
}
}
int main() {
/*test1();
test2();*/
priority_queue_heap<int> enter1();
enter1.insert(135);
enter1.insert(909);
enter1.insert(203);
cout<<endl;
cout<< "values to be removed" << endl;
cout << enter1.remove() << endl;
}
heap.h:
#ifndef HEAP_H
#define HEAP_H
/**
* This class implements a heap as described in the text.
* We will treat it as a priority queue.
*/
template <class T>
class heap {
public:
static const int CAPACITY = 10;
heap() {
size = 0;
}
bool is_empty() const { return size == 0;}
bool is_full() const { return size == CAPACITY; }
/**
* Remove the largest value from this heap and return it.
*
* Precondition: heap is not empty.
*/
T remove();
/**
* Inserts the 'value' into the heap.
*
* Precondition: heap is not full
*/
void insert(const T& value);
/**
* Check if the heap is valid.
* Prints out each parent and its children (for all nodes with children)
* Stops when a parent is less than one or both of its children
* Prints 'check' for each parent that is greater than or equal to its
children
*/
bool check_heap();
private:
T data[CAPACITY];
int size;
};
#include "heap.template"
#endif // HEAP_H
heap.template:
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <iomanip>
/*
* parent index is p, children are at indices 2*p+1 and 2*p+2
* You must check that those are in range
*
* child index is c, parent index is (c-1)/2 (integer division)
*/
/**
* Inserts the 'value' into the heap.
*
* Precondition: heap is not full
*/
template <class T>
void heap<T>::insert(const T& value) {
assert(!is_full());
//std::cout << size << std::endl;
// add the value to a new node in proper position
data[size] = value;
size++;
// move the value up the tree as needed
int child = size-1; // index of the new 'node'
int parent = (child-1)/2; // index of the parent
while((child > 0) && (data[parent] < data[child])) {
// swap parent and child values
T tmp = data[parent];
data[parent] = data[child];
data[child] = tmp;
// update parent and child
child = parent; // this is where new value is!
parent = (child-1)/2;
}
// it's a heap!
}
/**
* Remove the largest value from this heap and return it.
*
* Precondition: heap is not empty.
*/
template <class T>
T heap<T>::remove() {
assert(!is_empty());
// grab first element, save it for return later
T save = data[0];
// copy last value in list to the beginning
// decrement size
data[0] = data[size-1];
size--;
// size--;
// data[0] = data[size];
// sift the new first element down until it finds its place
int parent = 0;
int left_child = 2*parent+1;
int right_child = 2*parent+2;
bool still_working = true;
while(still_working && left_child < size) { // while the parent has at
least one child
if(right_child >= size) {
// only the left child to worry about
if(data[parent] < data[left_child]) {
// out of order, so swap them
T t = data[parent];
data[parent] = data[left_child];
data[left_child] = t;
parent = left_child;
still_working = false; // we must be done!
} else {
still_working = false;
}
} else {
// two children
if(data[left_child] > data[right_child]) {
//left child larger
if(data[parent] < data[left_child]) {
// out of order, so swap them
T t = data[parent];
data[parent] = data[left_child];
data[left_child] = t;
parent = left_child;
} else {
still_working = false;
}
} else {
// right child larger
if(data[parent] < data[right_child]) {
// out of order, so swap them
T t = data[parent];
data[parent] = data[right_child];
data[right_child] = t;
parent = right_child;
} else {
still_working = false;
}
}
left_child = 2*parent + 1;
right_child = 2*parent + 2;
}
}
return save;
}
/**
* Check if the heap is valid.
* Prints out each parent and its children (for all nodes with children)
* Stops when a parent is less than one or both of its children
* Prints 'check' for each parent that is greater than or equal to its
children
*/
template <class T>
bool heap<T>::check_heap() {
for(int p = 0; p < size; p++ ) {
int cl = 2*p+1;
int cr = 2*p+2;
std::cout << std::setw(5) << p << std::setw(10) << data[p];
if(cl < size) { // p has a left child?
std::cout << std::setw(10) << data[cl];
if(data[p] < data[cl]) {
std:exit(1);
}
}
if(cr < size) { // p has a right child?
std::cout << std::setw(10) << data[cr];
if(data[p] < data[cr])
std::exit(1);
}
std::cout << std::endl;
}
return true;
}
priority_queue_simple.template:
#include <cassert>
/**
* Remove the largest value from this priority queue and return it.
*
* Precondition: priority queue is not empty.
*/
template <class T>
T priority_queue_simple<T>::remove() {
assert(size > 0);
int imax = 0;
for(int i = 1; i < size; i++ ) {
if(data[i] > data[imax])
imax = i;
}
T tmp = data[imax];
data[imax] = data[size-1];
size--;
return tmp;
}
/**
* Inserts the 'value' into the priority queue.
*
* Precondition: priority queue is not full
*/
template <class T>
void priority_queue_simple<T>::insert(const T& value) {
assert(size < CAPACITY);
size++;
data[size-1] = value;
}
priority_queue_heap.h:
#ifndef PRIORITY_QUEUE_HEAP_H
#define PRIORITY_QUEUE_HEAP_H
//#include "heap.h"
template <class T>
class priority_queue_heap {
priority_queue_heap();
bool is_empty() const;
bool is_full() const;
/**
* Remove the largest value from this priority queue and return it.
*
* Precondition: priority queue is not empty.
*/
T remove();
/**
* Inserts the 'value' into the priority queue.
*
* Precondition: priority queue is not full
*/
void insert(const T& value);
private:
heap<T> pri_que;
};
#include "priority_queue_heap.template"
#endif // PRIORITY_QUEUE_HEAP_H
template <class T>
T priority_queue_heap<T>::remove()
{
return pri_que.remove();
}
priority_queue_heap.template:
template <class T>
T priority_queue_heap<T>::remove()
{
return pri_que.remove();
}
template <class T>
void priority_queue_heap<T>::insert(const T& value)
{
pri_que.insert(value);
}
priority_queue_simple.h:
#ifndef PRIORITY_QUEUE_SIMPLE_H
#define PRIORITY_QUEUE_SIMPLE_H
/**
* This class implements a priority queue using a very simple strategy:
* Store the values in an array.
* Add new values at the end.
* When asked to remove a value, search for the largest (linear search)
*
*/
template <class T>
class priority_queue_simple {
public:
static const int CAPACITY = 30;
priority_queue_simple() {
size = 0;
}
bool is_empty() const {
return size == 0;
}
bool is_full() const {
return size == CAPACITY;
}
/**
* Remove the largest value from this priority queue and return it.
*
* Precondition: priority queue is not empty.
*/
T remove();
/**
* Inserts the 'value' into the priority queue.
*
* Precondition: priority queue is not full
*/
void insert(const T& value);
private:
T data[CAPACITY];
int size;
};
#include "priority_queue_simple.template"
#endif // PRIORITY_QUEUE_SIMPLE_H
You should remove the "()" characters after enter1 at line 51 of main.cpp ...
Otherwise c++ sees that as a function, it does not call the constructor.
You have a subtle error in your heap declaration (main.cpp:57):
priority_queue_heap<int> enter1();
Here you are actually declaring a prototype for the enter1 function that takes no argument and returns a priority_queue_heap<int>. Just remove the parentheses to actually declare a variable:
priority_queue_heap<int> enter1;
priority_queue_heap<int> enter1();
Is interpreted by the compiler as a function named enter1 that returns a priority_queue_heap<int> and takes no parameters. When you use
cout << enter1.remove() << endl;
You are trying to call a member function on a name that the compiler interpreted as a function so that is why it tells you it is of non class type. Remove the () from enter1 so you have
priority_queue_heap<int> enter1;
and now enter1 will be an object of type priority_queue_heap<int>
I'm trying to delete "GoodBye" from the Remove function and then print a list with it missing.
I'm getting an error saying:
Error 1 error C2440: 'delete' : cannot convert from 'std::string' to 'void*
#include <iostream>
#include <string>
using namespace std;
const int SIZE = 5;
template <class New_Type>
class Array_Class
{
public:
Array_Class();
~Array_Class();
void Add(New_Type item);
int Search(New_Type item);
void Remove(New_Type item);
void Print();
private:
New_Type *A;
New_Type word;
int count;
};
template <class New_Type>
Array_Class<New_Type>::Array_Class()
{
cout << "You are inside the default constructor.\n";
cout << "New_Type has a size of " << sizeof(New_Type) << " bytes\n\n";
count = 0;
A = new New_Type[SIZE];
}
template <class New_Type>
Array_Class<New_Type>::~Array_Class()
{
cout << "The Destructor has been called.\n\n";
delete[] A;
count = 0;
A = 0;
}
template <class New_Type>
void Array_Class<New_Type>::Add(New_Type item)
{
if (count<SIZE)
{
A[count++] = item;
}
else
{
cout << "The array is full.\n";
}
}
template <class New_Type>
int Array_Class<New_Type>::Search(New_Type item)
{
int i;
for (i = 0; i<count; i++)
{
if (item == A[i])
{
return i;
}
}
return -1;
}
item is Goodbye. word will save the copy that gets deleted.
template <class New_Type>
void Array_Class<New_Type>::Remove(New_Type item)
{
int i;
word = item;
for (i = 0; i < count; i++)
{
if (item == A[i])
{
delete A[i];
}
}
}
template <class New_Type>
void Array_Class<New_Type>::Print()
{
int i;
for (i = 0; i<count; i++)
{
cout << "A[" << i << "] = " << A[i] << endl;
}
}
The main function which will add "GoodBye" and other words to my_String.
int main()
{
Array_Class<string> my_String;
Array_Class<int> my_Ints;
Array_Class<char> my_Chars;
my_String.Add("Hello");
my_String.Add("GoodBye");
my_String.Add("ComeHere");
my_String.Add("SayNo");
my_Chars.Add('a');
my_Chars.Add('b');
my_Chars.Add('c');
my_Chars.Add('d');
my_Chars.Add('e');
my_Chars.Add('f');
my_Chars.Add('g');
my_String.Print();
my_Ints.Print();
my_Chars.Print();
cout << endl;
my_String.Search("Hello");
my_String.Search("SayNo");
my_String.Remove will remove GoodBye from my_String:
my_String.Remove("GoodBye");
my_String.Print();
return 0;
}
The problem is that your Remove function should not be issuing any calls to delete. What it should be doing is shifting the elements "up" by one and decreasing the count member variable. This in effect "removes" the item from the Array.
To shift the elements up, you write a loop where you replace element i with element i+1, where you start the loop at the item you want to remove.
However your array is dynamically allocated, you can't just call delete on particular elements of it. This is a contiguous block of memory. You can do what #PaulMcKenzie said - To find element that matches element passed as an argument to Remove function and then shift to the left remaining array elements, then decrease count member variable. I solved it, but since this is homework posting it wouldn't be wise. Here's a very weird pseudo-code of mine. I hope you understand that concept.
//array elements : Hello, GoodBye, ComeHere, SayNo
my_String.Remove("GoodBye");
// found index of element to remove = 1;
// decrement count
// loop from saved index through count-1:
// A[i] = A[i+1];
// There will be two iterations of this loop. here's how array would look like:
// 1st: array elements : Hello, ComeHere, ComeHere, SayNo
// 2nd: array elements : Hello, ComeHere, SayNo, SayNo
Then, because of decrementing count, last element won't be printed.
And in c++, for dynamic arrays std::vector is way to go.
I have an assignment to create a template container class with a dynamic array that can be increased and decreased. When the array is displayed, I get either garbage numbers or no numbers at all for the elements in the array where it was expanded(so if it expanded on the fifth element, the value would turn to junk after I enter the sixth). When I try to remove elements I get a Debug Assertion Failed error.
Here is my code:
template <class T> class container {
public:
container(); // constructor
// Post-condition: count is set to -1 and dynamic array size set to 5
~container(); //destructor
void insert(T &n);
// Pre-condition: a value is passed to the function
// Post-condition: if data array is not full, increment count by 1 and insert the value in the array
// Otherwise, display "Container full! No insertion is made."
void remove();
//Post-condition: if data array is not empty, remove the data[count] element in data array and decrement count by 1;
//otherwise, display a message "Container empty! Nothing is removed from it."
void display();
// Post-condition: if the array is not empty, displays all values in data array similar to the sample output;
//Otherwise, display the message “Container is now empty!"
void fillarray(container c);
//pre-condition: a container c is passed to the function
//post-condition: dynamic array of chosen type is created and filled continuously with user entered values
private:
bool empty;
//Post-condition: returns true is the array is empty, otherwise returns false
T *data; // dynamically allocated array used to store or contain the inserted values
int count; // indicates how many values have been inserted
int max;
};
template <class T> container<T>::container()
{
count = -1;
max = 5;
data = new T[max];
assert(data != NULL);
}
template <class T> container<T>::~container()
{
delete [] data;
}
template <class T> void container<T>::insert(T &n)
{
if (count >= (max - 1))
{
max = max * 2;
cout << "\nContainer full! Array size is increased by " << max/2 << ".";
T *temp = new T[max];
assert(temp != NULL);
for (int i = 0; i < count; i++)
temp[i] = data[i];
delete [] data;
data = temp;
count++;
data[count] = n;
}
else
count++;
data[count] = n;
}
template <class T> void container<T>::remove()
{
empty = count < 0;
if (empty == 1)
{
cout << "\nContainer empty! Nothing is removed from it.";}
else
{
count--;
T *temp1 = new T[max];
assert(temp1 != NULL);
for (int i = 0; i < count; i++)
temp1[i] = data[i];
delete [] data;
data = temp1;
}
}
template <class T> void container<T>::display()
{
empty = count < 0;
if (empty == 1)
{
cout << "\nContainer is now empty!";}
else
{
for (int i = 0; i <= count; ++i)
cout << " " << data[i];
}
}
template <class T> void container<T>::fillarray(container c)
{
char ans;
do
{
T value;
cout << "\nEnter a value:";
cin >> value;
c.insert(value);
cout << "\nAfter inserting, the \"container\" contains:" << endl;
c.display();
cout << "\nEnter more? (Y/y or N/n)";
cin >> ans;
} while (ans == 'Y' || ans == 'y');
for (int i = 0; i <= count; i++)
{
c.remove();
cout << "\nAfter removing a value from it, the \"container\" contains:" << endl;
c.display();
cout << endl;
}
}
// The main driver function to be used to test implementation
int main()
{
char choice;
cout << "\nEnter S for string container, D for double";
cin >> choice;
if (choice == 'S' || choice == 's')
{
container<string> c;
c.display();
c.fillarray(c);
}
else if(choice == 'D' || choice == 'd')
{
container<double> c;
c.display();
c.fillarray(c);
}
return 0;
}
template <class T> void container<T>::fillarray(container c)
{
//...
}
This function actually involves two container<T> objects: *this and c.
Since you "pass by value" (the function parameter is not a reference), c in fillarray is created as a copy of the original c in main. In fillarray you modify c, which deletes and changes c.data, but this->data still contains the dangling pointer to the original storage. Before long, you get undefined behavior; luckily enough bad things happened you could tell something was wrong.
Per the Rule of Three (Plus Two), if a class has a destructor, you probably should not allow the compiler to generate the default copy constructor and copy assignment, and you may want to consider implementing a move constructor and move assignment.
The easiest, and sometimes best, way of meeting that rule is to disable copying:
template <class T> class container
{
public:
container(container const&) = delete;
container& operator=(container const&) = delete;
//...
};
Then the compiler will make sure you don't accidentally make a copy and get yourself into this sort of trouble.
You make life confusing by count referring to maximum index rather than the number of entries. Using your scheme though, this line in insert is odd:
for (int i = 0; i < count; i++)
As that won't copy the final entry.
Basically a dynamic array, which has circular rotation when is full. You have access to every element and you can change it's value, however you can insert and remove only from both ends.(constant time). Most of the methods seem to be working fine, however at certain "push" numbers I get wrong output.
For example first input is 1,2,3 then I insert 4 at the end. Next output is: 2,3,4 However after I insert 5 at the end the output is 2, 3, 5
I have no idea what is causing this. I am posting the entire source code below (atleast the functions which have to do with the tests where the error must hide). There is some documentation in the file and an example of the error in case I haven't explained things clearly.
#include <iostream>
using namespace std;
template <typename Object>
class ArrayVector {
private:
int capacity; // capacity
int sz; // number of elements
Object* a;
int f; // start of the indexes
int b; // end of the indexes
public:
ArrayVector(int initCap);
~ArrayVector();
int size() const { return sz; }
bool isEmpty() const { return size() == 0; }
Object elemAtRank(int r);
void pushBack( const Object& e);
void pushFront(const Object& e);
void popBack();
void popFront();
};
template <typename Object> // constructor
ArrayVector<Object>::
ArrayVector(int initCap) {
capacity = initCap;
sz = 0;
a = new Object[capacity];
f = 0;
b = 0;
}
template <typename Object> // gets the element at a certain rank
Object ArrayVector<Object>:: elemAtRank(int r)
{
return a[(f + r) % sz]; // starting position in real array + r % number of elements
}
template <typename Object>
void ArrayVector<Object>:: pushBack( const Object& e)
{
if(sz == capacity && sz > 0) // if the array is full time to spin it
{
if(f == capacity){ // Handles the front.
f = 0; // if the front is equal to the capacity
// set it to zero, else increment
}else{
f++;
}
if(b == capacity){ //Handles the back
b = 0; //if the back is equal to the capacity
// cout<< "SC insert "<< e << " at "<< b <<endl;
a[b] = e;
}else{ // set it to zero, else increment
a[b] = e;
// cout<< "SC insert "<< e << " at "<< b <<endl;
b++;
}
}else{
a[b] = e;
// cout<< "insert "<< e << " at "<< b <<endl;
b++;
sz++;
}
}
template <typename Object>
void ArrayVector<Object>:: pushFront( const Object& e)
{
if(f == 0){
f = capacity-1;
}else{
f--;
}
a[f] = e;
if(sz< capacity)
sz++;
}
int main()
{
// Fill array and print it
cout << "Fill with numbers" << endl;
ArrayVector<int> asd(3);
asd.pushBack(1);
asd.pushBack(2);
asd.pushBack(3);
for(int i =0; i < asd.size(); i++)
cout << asd.elemAtRank(i) << endl;
//Test if it spins
cout << "BEGIN Spin TEST " << endl;
asd.pushBack(4);
cout << "First test is ok" << endl;
for(int i =0; i < asd.size(); i++)
cout << asd.elemAtRank(i) << endl;
// here the error comes
asd.pushBack(5);
cout << "On the second iteration things crash and burn" << endl;
for(int i =0; i < asd.size(); i++)
cout << asd.elemAtRank(i) << endl;
return 0;
}
In addition to your insertion times not matching your desired requirements, your issue is here:
template <typename Object>
void ArrayVector<Object>:: pushFront( const Object& e)
{
if(f == 0)
{
f = capacity-1;
}
else
{
f--;
}
a[f] = e; // problem lies here!
if(sz < capacity)
sz++;
}
You are figuring out where to do the insert, but you are not pushing the other elements around the vector; that is, you are just overwriting the element at the insertion point. If you wanted to push it onto the front, you would need to copy the other elements over 1 position each and then do your insert. A better solution (which would match your constant insertion time requirement) would be to implement it as a double-linked list. Pushing onto the ends would simply require the following pseudo-code:
void push_front(const Object& o)
{
if (size == capacity)
l.pop_back();
l.push_front(o);
}
If you really must use a contiguous memory block, you will not get constant-time insertion, but it would look like this:
// Assumptions: 0 is always the front, capacity-1 is always the maximum back
template <typename Object>
void ArrayVector<Object>:: pushFront( const Object& e)
{
// assume capacity > 0, move all the elements to the right one slot
for (int i = capacity - 1; i > 0; --i)
{
a[i] = a[i - 1];
}
a[0] = e;
if(sz < capacity)
sz++;
}
Below are my .cpp file and .h file. After getting a lot of help from Mike, I finally get it working; however, when I compile it on Visual Studio 2012, it gives me 2 warnings about '<' signed/unsigned mismatch right on the line "for (int i = 0; i < s.length(); i++)". Can anyone tell me what I did wrong in there ?
[code]
#include"DownwardStack.h"
#include<iostream>
#include<string>
#include<memory>
#include<cassert>
using namespace std;
unique_ptr<string> reverse_string(string const &s);
int main()
{
int count = 0;
string s;
unique_ptr<string> reverse(new string());
unique_ptr<DownwardStack<int>> ptr(new DownwardStack<int>());
cout << "Your string: ";
cin >> s;
reverse = reverse_string(s);
cout << "Your reverse string is: " << *reverse << endl;
if(ptr->IsEmpty())
cout << "ptr is empty" << endl;
else
cout << "ptr is not empty" << endl;
assert(ptr->IsEmpty());
assert(!ptr->IsFull());
ptr->Push(5);
ptr->Push(7);
ptr->Push(10);
ptr->Push(15);
ptr->Push(4);
cout << "Stack size: " << ptr->GetSize() << endl;
cout << "Top element: " << ptr->Peek() << endl;
cout << "Pop one element out." << endl;
ptr->Pop();
cout << "Top element: " << ptr->Peek() << endl;
return 0;
}
unique_ptr<string> reverse_string(string const &s)
{
DownwardStack<char> stack;
cout << s.length() << endl;
// Here it gives me a warning on the for loop
for (int i = 0; i < s.length(); i++)
{
stack.Push(s[i]);
}
unique_ptr<string> result(new string);
// Again it gives me a warning on the for loop
for(int i = 0; i < s.length(); i++)
{
*result += stack.Peek();
stack.Pop();
}
return result;
}
[/code]
This is my header file .h
[code]
#pragma once
#include<cassert>
#include<stack>
#include<string>
// size: number of elements inside the array
const int FIXED_ARRAYED_STACK_CAPACITY = 100;
template<class T>
class DownwardStack
{
public:
DownwardStack();
~DownwardStack();
// 1 step
// O(0)
int GetSize() const {return size;}
bool IsEmpty() const {return (size==0);}
bool IsFull() const {return (size==FIXED_ARRAYED_STACK_CAPACITY);}
T Peek();
void Pop();
void Push(T val);
void Clear();
void DisplayStack();
private:
int size;
T elements[FIXED_ARRAYED_STACK_CAPACITY];
};
// O(1)
template<class T>
DownwardStack<T>::DownwardStack()
{
size = 0;
}
template<class T>
DownwardStack<T>::~DownwardStack()
{
}
// assert = 1 step
// IsEmpty() = 1 step
// total = 2 steps
// f(n) = 2
// O(1)
template<class T>
T DownwardStack<T>::Peek()
{
assert(!IsEmpty());
return elements[FIXED_ARRAYED_STACK_CAPACITY - size];
}
// In order to take something out, it must not be empty
// assert = 1 step
// IsEmpty() = 1 step
// size-- = 1 step
// total = 3 steps
// O(1)
template<class T>
void DownwardStack<T>::Pop()
{
assert(!IsEmpty());
size--;
}
// In order to put in something, the stack must not be full
// assert = 1 step
// IsFull = 1 step
// assignment = 1 step
// size++ = 1 step
// total = 4 steps
// O(1)
template<class T>
void DownwardStack<T>::Push(T val)
{
assert(!IsFull());
elements[FIXED_ARRAYED_STACK_CAPACITY - size - 1] = val;
size++;
}
template<class T>
void DownwardStack<T>::Clear()
{
size = FIXED_ARRAYED_STACK_CAPACITY;
assert(IsEmpty());
}
[/code]
I would implement this as
std::string reverse_string(std::string const & s) {
return {s.rbegin(), s.rend()}; // C++11 or later
return std::string(s.rbegin(), s.rend()); // historical dialects of C++
}
If you really must make life difficult for yourself by using a stack: push each character into it, then pop each out into the new string. By the nature of stacks, you'll pop the characters in the reverse order.
std::string reverse_string(std::string const & s) {
DownwardStack<char> stack;
// write a loop to push each character of "s" onto "stack"
std::string result;
// write a loop to pop each character from "stack" into "result"
return result;
}
If you absolutely have to return a unique pointer, (which you really, really shouldn't), then that becomes
std::unique_ptr<std::string> reverse_string(std::string const & s) {
DownwardStack<char> stack;
// write a loop to push each character of "s" onto "stack"
std::unique_ptr<std::string> result(new std::string);
// write a loop to pop each character from "stack" into "*result"
return result;
}
And do the world a favour: only use new when you actually need it. There's no reason to use unique_ptr either for the stack (make that automatic), or the return value (since std::string is movable).