I'm having trouble figuring out the destructor for my hashTable class, the destructor is like this:
template <typename ElementType>
HashSet<ElementType>::~HashSet() noexcept
{
for (unsigned int i=0;i<hashCapacity;i++)
{
Node* current = hashTable[i];
while(current != nullptr)
{
Node* entry = current;
current = current->next;
delete[] entry;
}
}
delete[] hashTable;
}
No matter I use either delete[] or delete, it gives me either double-free errors or segmentation fault.
The class template is below:
template <typename ElementType>
class HashSet : public Set<ElementType>
{
public:
// The default capacity of the HashSet before anything has been
// added to it.
static constexpr unsigned int DEFAULT_CAPACITY = 10;
// A HashFunction is a function that takes a reference to a const
// ElementType and returns an unsigned int.
using HashFunction = std::function<unsigned int(const ElementType&)>;
public:
// Initializes a HashSet to be empty so that it will use the given
// hash function whenever it needs to hash an element.
explicit HashSet(HashFunction hashFunction);
// Cleans up the HashSet so that it leaks no memory.
~HashSet() noexcept override;
// add() adds an element to the set. If the element is already in the set,
// this function has no effect. This function triggers a resizing of the
// array when the ratio of size to capacity would exceed 0.8, in which case
// the new capacity should be determined by this formula:
//
// capacity * 2 + 1
//
// In the case where the array is resized, this function runs in linear
// time (with respect to the number of elements, assuming a good hash
// function); otherwise, it runs in constant time (again, assuming a good
// hash function). The amortized running time is also constant.
void add(const ElementType& element) override;
Where my add function and default constructor implementation is like this:
template <typename ElementType>
HashSet<ElementType>::HashSet(HashFunction hashFunction)
: hashFunction{hashFunction}
{
hashCapacity = DEFAULT_CAPACITY;
hashSize = 0;
hashTable = new Node* [hashCapacity];
for (int i=0;i<hashCapacity;++i)
{
hashTable[i] = nullptr;
}
}
template <typename ElementType>
void HashSet<ElementType>::add(const ElementType& element)
{
if (contains(element)==false)
{
if ((hashSize/hashCapacity) > 0.8)
{
}
else
{
unsigned int index = hashFunction(element) % hashCapacity;
hashSize += 1;
Node* add = new Node;
add->next = nullptr;
add->value = element;
if (hashTable[index]==nullptr)
{
hashTable[index] = add;
}
else
{
Node* addNode = hashTable[index];
while(addNode->next != nullptr)
{
addNode = addNode->next;
}
addNode->next = add;
}
}
}
}
Note: that resize hashtable part is incomplete because I'm examining the functionality for my hash table to hold a small amount of value first.
Related
I'm trying to implement a class of Circular List with a nested class of iterator and I wrote like this:
template <class T>
class CircularList {
struct Item {
T data;
Item* next;
};
Item* head;
int size;
public:
CircularList() {
head = new Item();
head->next = head;
}
int sizeList() { return size; }
void push(T data) {
Item* i = new Item();
i->data = data;
i->next = head->next;
head->next = i;
size++;
}
class CircularListIterator {
Item* p;
CircularListIterator() {
p = head->next;
}
bool hasNext() {
if(p->next != head) {
return true;
}
return false;
}
T next() {
T data_temp = p->data;
p = p->next;
return data_temp;
}
};
CircularListIterator* iterator() {
return new CircularListIterator();
}
};
int main() {
CircularList<string>* letters = new CircularList<string>;
letters->push("d");
letters->push("c");
letters->push("b");
letters->push("a");
Iterator<string>* it= new Iterator<string>;
it = letters->iterator();
while (it->hasNext()) {
cout<< it->next() << "," << endl;
}
return 0;
}
But the Iterator is not working when I try to create an iterator in the main function, It said that it wasn't declared in the scope and has no member of it.
Assuming by "in the main class" you mean in the main function, the problem is quite straightforward: you're trying to construct a ::Iterator<string>, but there is no class in the global namespace (or anywhere else, in this code sample) called Iterator! You could try constructing a CircularList<string>::CircularListIterator - that's at least a class that exists - but it wouldn't work because the iterator needs to be associated with a CircularList object for it to be able to access member variables like head.
The correct thing to do here is to promote the iterator function - the one that returns a CircularListIterator* - out of the CircularListIterator class and into the CircularList class. Then, in your main function, you can call letters->iterator() and it'll return a CircularListIterator* for the letters object.
Now, CircularListIterator doesn't inherit from any other iterator classes - neither the (nonexistent-in-this-code Iterator you've typed it as, nor the C++ std::iterator or any of its variants) - so you can't assign it to it or probably even compile the code that references Iterator. To make CircularListIterator a subclass of std::iterator, you'll need to extend std::iterator<Category, T> with the appropriate category. See https://www.cplusplus.com/reference/iterator/iterator/ for more information on the std::iterator class template, including an example of implementing it.
Class:
template <class T>
class vectorADT
{
public:
//default constructor
vectorADT();
//destructor
~vectorADT();
//push data to the front of the vector
void push_front(T data);
//push data to the rear of the vector
void push_back(T value);
void insert(int position, T value);
//remove data from the front of the vector
void remove_front();
//remove data from the rear of the vector
void remove_rear();
//return the front of the vector
T getFront();
//return the rear of the vector
T getRear();
//check if vector is full
bool isFull();
//create a new vector with more space
T *resize(T *prevSizePtr);
//return the size of the vector
int size();
//check if the vector is empty
bool isEmpty();
//print the vectors data
void print();
private:
T *vectPtr;
T array[4] = {};
int front;
int rear;
int vectSize;
};
constructor:
template <class T>
vectorADT<T>::vectorADT()
{
front = 0;
rear = -1;
vectSize = 4;
vectPtr = array;
}
Class Method:
template <class T>
void vectorADT<T>::push_front(T data)
{
if (vectPtr[0] == nullptr)
{
vectPtr[front] = data;
}
front++;
}
I asked a question earlier but I am still really confused on how this works. I want to be able to check if my array has nullptr as a value, that way I know whether that index is empty, and if it is, I can assign that index some data. Whenever I try to make the comparison to nullptr, I get a host of errors about operator==. I thought that when I do T array[4] = {} it would initialize all the index to nullptr or zero, thus making it valid to compare that index with nullptr, however that obviously is not the case. If anyone could point me in the right direction on how I would go about doing something like that I would really appreciate it. Thank you.
Re: I want to be able to check if my array has nullptr as a value, that way I know whether that index is empty - You really don't need to know that. You should maintain a size of your vector that would tell you if there are available slots and where they are. What you call vectSize is in fact its capacity.
i was confused with a problem of nested class
#ifndef MINIGRAPH_H_
#define MINIGRAPH_H_
#include<vector>
#include<list>
#include<iostream>
template <typename VEX,typename EDGE>
class MiniGraph
{
public:
class _Node
{
public:
VEX _Vex;
EDGE _Edge;
_Node* next;
};
MiniGraph() {};
MiniGraph(int vex_num);
void add(VEX from, VEX to, EDGE Edge);
//void display();
private:
std::vector<void*> _VecNode;
_Node* NewNode(VEX vex) { _Node* ptr = new _Node;ptr->_Vex = vex;return ptr; }
_Node* NewNode(VEX vex, EDGE edge) { _Node* ptr = new _Node;ptr->_Vex = vex;ptr->_Edge = edge;return ptr; }
};
template <typename VEX, typename EDGE>
MiniGraph<VEX, EDGE>::MiniGraph(int vex_num)
{
int i = 0;
for (i = 0;i < vex_num;i++)
{
struct Node *ptr_tmp = new _Node;
ptr_tmp->next = NULL;
_VecNode.push_back(ptr_tmp);
}
}
template <typename VEX, typename EDGE>
void MiniGraph<VEX, EDGE>::add(VEX from, VEX to, EDGE edge)
{
int i;
_Node* ptr_node = NULL;
for (i = 1;i < _VecNode.size();i++)
{
ptr_node = (_Node*)_VecNode[i];
if (ptr_node->_Vex == from)
break;
}
if (i == _VecNode.size())
{
ptr_node = NewNode(from);
_VecNode.push_back(ptr_node);
}
ptr_node = NewNode(from, edge);
**ptr_node->next = (_Node*)_VecNode[i]->next;//insert node from head**
_VecNode[i]->next = ptr_node;
}
when compiled with
#include "MiniGraph.h"
void main()
{
MiniGraph<int, double> hh;
hh.add(1, 2, 0.1);
hh.add(1, 3, 0.2);
hh.add(2, 3, 0.3);
}
error triggered in ptr_node->next = (_Node*)_VecNode[i]->next;//insert node from head
saying ->next left must point to class/struct/union/generic type;
but _Node was declared 。i was tring to substitute class by struct,but it triggered the same error.
it seems that declaration is not visible.how should i solve this problem?
As #WhozCraig mentioned in the comments above, you have a vector of void*, not a vector of Node*. Therefore there is no member called next. You can type_cast the void* to a Node* to "fix" the problem, but you would be in for a surprise.
The problem start from your main function.
int main()
{
MiniGraph<int, double> hh;
This code will call the default constructor of the MiniGraph class. This means that no Nodes will be created and your vector of Node* will be empty (i.e. std::vector<Node*>::size = 0.
Then you call the add function:
hh.add(1, 2, 0.1);
Inside the add function the following happens:
void add(VEX from_, VEX to_, EDGE edge_)
{
std::size_t i = 0;
for (i=1; i<node_ptrs.size(); i++)
{
if (node_ptrs[i]->vex == from_)
break;
}
After the first call to add(), the vector size = 0, but i = 1. That's because you initialize i=1 inside the for-loop.
Then you check if i == vector::size.
Node *tmp = nullptr;
if (i == node_ptrs.size())
{
tmp = NewNode(from_);
node_ptrs.push_back(tmp);
}
The if-statement above will never be executed, because i = 1 and vector::size = 0.
Finally, you do the following:
tmp = NewNode(from_, edge_);
tmp->next = node_ptrs[i]->next; // this fails because your vector size is 0!
node_ptrs[i]->next = tmp;
The first line works. tmp is a Node* which points to a newly created Node. ok. Then the program fails with a segmentaion fault because tmp->next is trying to shallow copy the pointer in the i element of the vector. Remember now that i = 1 and the vector is still empty, i.e. vector::size = 0. There is no node_ptrs[1] element to copy from. And that's why it crashes.
Example of the code above: https://rextester.com/DTODK86272
There are more problems to consider in your original code... For example:
if only one pointer is meant to point at a certain Node, then use a unique_ptr. You can still have other pointer reading from a unique_ptr, but they won't be able to do anything else.
write constructors that initialize your pointers to nullptr and call a zero initialization on the vex and edge members.
Avoid using int as array indices. What if someone decides to use a negative number as a function argument?
In your implementation you must check if the vector is empty before you start doing anything else. What should happen if the vector is empty?
I keep getting a segmentation fault and am not sure why. Pretty new to templates and am just messing around trying to figure things out. I built a stack using a template, and have only incorporated the member functions push and top/peek so far. Trying to create a string stack.
#include <iostream>
#include <string>
template <class T>
class TemplateStack {
public:
typedef T type;
TemplateStack()//Default Constructor taking no parameters
{
max_size_ = 50;
TopOfStack = 0;
}
void push(T element)
{
if (TopOfStack == max_size_)
throw string("Stack's underlying storage is overflow");
TopOfStack++;
data_[TopOfStack] = element;
}
T top() {
if (TopOfStack == -1)
throw string("Stack is empty");
return data_[TopOfStack];
}
private:
size_t TopOfStack; //Generic data type for the top element of stack
size_t max_size_;
T* data_;
};
int Main (){
TemplateStack <string> T;
T.push("Hello");
T.push("World!");
std::cout<<T.top()<<std::endl;
return 0;
};
You did not allocate memory for the data member
T* data_;
in the constructor
TemplateStack()//Default Constructor taking no parameters
{
max_size_ = 50;
TopOfStack = 0;
}
So any operation with the stack that uses the pointer results in undefined behavior.
And the initial value of the data member TopOfStack according to the implementation of other methods shall be -1.
I think you mean at least
TemplateStack()//Default Constructor taking no parameters
{
max_size_ = 50;
TopOfStack = -1;
data_ = new T[max_size];
}
If so you need also to write a destructor to free allocated memory.
And the method push shall check another condition
void push(T element)
{
if (TopOfStack + 1 == max_size_)
throw string("Stack's underlying storage is overflow");
TopOfStack++;
data_[TopOfStack] = element;
}
Or as the data member TopOfStack has the type size_t then you could write
TemplateStack()//Default Constructor taking no parameters
{
max_size_ = 50;
TopOfStack = 0;
data_ = new T[max_size];
}
void push(T element)
{
if (TopOfStack == max_size_)
throw string("Stack's underlying storage is overflow");
data_[TopOfStack++] = element;
}
and
T top() {
if (TopOfStack == 0)
throw string("Stack is empty");
return data_[TopOfStack -1];
}
I am having trouble coding Copy constructor for C++ HashTable. Now below is the class structure
template <class TYPE>
class HashTable : public Table<TYPE>
{
struct Record
{
TYPE data_;
string key_;
Record* Next;
Record(const string& key, const TYPE& data)
{
key_ = key;
data_ = data;
Next = nullptr;
}
Record(const Record& a) {
if(!a.key_.empty()){
if(a.Next == nullptr){
Next = nullptr;
}
else
{
Record* temp = a.Next ;
Record *temp2 = Next;
while(temp != nullptr)
{
temp2 = temp ;
temp = temp->Next ;
}
temp2->Next = nullptr;
}
data_ = a.data_ ;
key_ = a.data_ ;
} // user-
};
int TableSize;
Record** records;
}
};
and below is the copy constructor
template
HashTable<TYPE>::HashTable(const HashTable<TYPE>& other)
{
records = new Record*[other.TableSize];
TableSize = other.TableSize;
for(int i = 0 ; i < other.TableSize; i++)
records[i]= (new Record(*other.records[i]));
}
I have also posted the code on ideone http://ideone.com/PocMTD. The code for copy constructor seems to be crashing. I don't see any memory leak that will cause the program to crash. I have tried memcopy, using the insert function and the all seems to fail.
Replace int TableSize; and Record** records; with std::vector<std::unique_ptr<Record>>
In Record, change Record* Next; to Record* Next=nullptr;.
Stop calling new.
Include HashTable(HashTable&&)=default;.
HashTable<TYPE>::HashTable(const HashTable<TYPE>& other)
{
records.reserve( other.records.size() );
for (auto const& rec_in : other.records)
records.emplace_back( new Record(*rec_in) ); // make_shared<Record> in C++14
}
Now we are no longer doing manual memory management. So an entire set of worries is gone.
Next, look at that raw Next pointer. It is bad news. When you copy a Record, the Next pointer points into the old set of Record structures.
We can fix this in a few ways. The slickest is to use an offset pointer.
template<class T>
struct offset_ptr {
std::ptrdiff_t offset = std::numeric_limits<std::ptrdiff_t>::max();
explicit operator bool()const {
return offset!=std::numeric_limits<std::ptrdiff_t>::max();
}
T* get() const {
return (T*)( offset+(char*)this );
}
T* operator->() const { return get(); }
T& operator*() const { return *get(); }
operator T*() const { return get(); }
offset_ptr(std::nullptr_t):offset_ptr() {}
explicit offset_ptr(T* p) {
if (!p) return;
offset = (char*)p-(char*)this;
Assert(*this);
}
offset_ptr()=default;
offset_ptr(offset_ptr const&)=default;
offset_ptr& operator=(offset_ptr const&)=default;
offset_ptr(offset_ptr&&)=default;
offset_ptr& operator=(offset_ptr&&)=default;
};
which instead of storing a pointer by absolute location, stores an offset.
Now we do this:
template<class TYPE> struct Table{};
template <class TYPE>
class HashTable :public Table<TYPE>
{
public:
struct Record
{
TYPE data_;
std::string key_;
offset_ptr<Record> Next;
Record(const std::string& key, const TYPE& data)
{
key_ = key;
data_ = data;
Next = nullptr;
}
Record(const Record& a)
{
if(!a.key_.empty())
{
if(a.Next == nullptr)
{
Next = nullptr;
}
else
{
auto temp = a.Next;
while(temp != nullptr)
{
Next = temp;
temp = temp->Next;
}
}
data_ = a.data_;
key_ = a.data_;
}
}
};
std::vector<Record> records;
};
and no copy ctor is needed; the offset ptr knows the location of the other record as an offset within the records. Data is stored by-value instead of by-reference.
Note that we have a vector of Records, not pointers-to-Records. This is key for the offset_ptr to work. Resizing isn't a problem, as the offsets remain the same. Copying remains safe, as offsets on each side now refer to other elements within their vector. Inserting/removing in the middle is dangerous, but simply nulling elements is not.
Note that buffers of size max std::ptrdiff_t or beyond are not supported by the above offset_ptr. On a 64 bit system that is about 2 gigs; on a 64 bit system it is large. (I don't use 0 for the null value, because if I did then an offset_ptr<X> as the first member of a struct X would nullify if I ever made it point to its enclosing X.)
boost also has a less bespoke offset_ptr type. The implementation above is meant as a sketch of how easy it is, not a solid implementation.
You do not show the complete code here (neither on ideone), but let me take a guess based on what I see.
I assume that your other object, which you pass in the copy c'tor has a fully set up list of Records.
I further assume that your HashTable class has a destructor (not shown) which deletes all the linked Records.
Your copy constructor calls the copy c'tor of Record(for each entry in the array of pointers to Record). The Record coyp c'tor only makes a shallow copy, i.e. only the pointer to the next element is copied (it will still point to the next element of the copied Record from the other hash table.
Thus, when other and its copy are deleted (at the end of scope or program; not shown), you will have double deletion (crash).
Fix: Make sure that Record has correct copy constructor, copy assignment and destructor (maybe even move c'tor and move assignment) (rule of five).
The same applies for the HashTable class as well.
Better fix: Use std::unordered_map.