i have a template array consist Cells that holds the data, described in the code:
template <class T>
class Array
{
private:
//the array is consist cellss that holds the data
template<class S>
class Cell
{
public:
//members:
S* m_data;
//methods:
//C'tor:(inline)
Cell(S* data=NULL): m_data(data){};
//D'tor:(inline)
~Cell(){delete m_data;};
//C.C'tor:(inlnie)
Cell(const Cell<S>& cell): m_data(cell.m_data){};
};
private:
//members of Array:
Cell<T>* m_head,*m_last;
unsigned int m_size;
public:
/*******************C'tors and D'tors************************/
//C'tor:(inline)
Array():m_head(NULL),m_last(NULL), m_size(0){};
//D'tor:
~Array(){delete[] m_head;};
//C.C'tor:
Array(const Array& array): m_head(array.m_head),m_last(array.m_last),m_size(array.m_size){};
/****************Adding********************/
//add an element to the end of the Array:
void add(const T added);
/*******************Returning and deleting***********************/
T& operator[](const unsigned int index)const {return *(m_head[index].m_data);};
//delete the last element:
void remove();
/*********************Size*****************************/
//returning the number of elements:(inline)
const unsigned int size()const{return m_size;};
//check if the Array is empty:
bool isEmpty()const {return (m_size==0);};
};
now this is the implementaion of add:(after tests look like it works fine but just for case i write it here also)
template <class T>void Array<T>::add(const T added)
{
//allocating memory for the new array:
Cell<T>* newarray=new Cell<T>[m_size+1];
//copy all the elements from the old array:
unsigned int i;
for (i=0; i<m_size;i++)
newarray[i].m_data=m_head[i].m_data;
//put the added in the last index:
T* newelement= new T(added);
newarray[i].m_data=newelement;
//change the ptrs:
m_head=newarray;
m_last=&newarray[m_size];
//increase the counter:
m_size++;
}
and this is the implementaion of remove:
template <class T>void Array<T>::remove()
{
//if there is only 1 element:
if(m_size==1)
{
delete[] m_head;
m_head=m_last=NULL;
}
//change the last the previus cell
else
{
delete m_last;
m_last=m_last-1;
}
//and decrease the counter:
m_size--;
}
now when do:
Array<int> a;
a.add(3);//work fine
a.add(4);//work fine
a.remove();//fail
i get a runtime error from the line delete m_last; even though m_last point to a cell that actually hold data (m_last point to a cell holds 4).
what am i missing here? why cant i delete a pointer to a cell in the Array?
the error VS2012 give me:_BLOCK_TYPE_IS_VAILED(pHead->nBlockUse)
another important thing i forgot to say: when debug it doesnt enter the D'tor of Cell at all, it just get out when going to the delete.
You can't delete one element of array.
int *x = new int[10];
delete &x[2] ; // It is incorrect!
You can only delete whole of array:
delete [] x;
The destructor ~Cell calls delete. That's a sure sign that the constructor should call new.
Related
I have a class and I want to write a generic sorted list that we can use it with that class:
class A
{
int n;
public:
A(int n):n(n){}
};
and this how I thought to do my sorted list class
template <class T>
class SortedList
{
T* data;
int size;
int max_size;
void expand();
static const int EXPAND_RATE=2;
static const int INITIAL_SIZE=10;
public:
SortedList();
};
template <class T>
SortedList<T>::SortedList():data(new T[INITIAL_SIZE]),size(0),max_size(INITIAL_SIZE){}
// ^^^ here we need a T()
now the problem is that class A doesn't have a c'tor like this A()
does anyone have any Idea how can I write the sortedlist class without needing the A()??
PS: some people have recommended to do Nodes with T data and next but I can't see how can I do this, and how could this help? since we still need a T()
Edit: I tried now to do it this way:
template <class T>
class SortedList
{
T** data;
// ^^
int size;
int max_size;
void expand();
static const int EXPAND_RATE=2;
static const int INITIAL_SIZE=10;
public:
SortedList();
};
template <class T>
SortedList<T>::SortedList():data(new T*[INITIAL_SIZE]),size(0),max_size(INITIAL_SIZE){}
now I am facing a different problem with this function:
void SortedList<T>::insert(const T& object)
{
if(size>=max_size)
{
expand();
}
int index=0;
for(int i=0;i<size;i++)
{
T item=*data[i];
// when I try to print item nothing goes out
if(item<object)
// ^^^ here I get a segmentation fault
{
continue;
}
index=i;
break;
}
size++;
for (int i = size-1; i >index; i--)
{
data[i]=data[i-1];
}
T Item(object);
T* ptr= &Item;
data[index]=ptr;
//when I print *data[index] it does get printed perfectly
}
does anyone have any Idea how can I write the sortedlist class without needing the A()??
Option 1: Allocate memory for each element separately. In other words, use a node based data stucture such as a linked list.
Option 2: Separate allocation of memory from creation of the object. This can be achieved by using std::allocator to allocate the memory, and std::construct_at to create the object (use placement-new instead prior to C++20). This is how dynamic array-like data structures such as std::vector are implemented.
P.S. Manual memory management is challenging. If you attempt to do it without studying the subject in depth, you'll very likely write a broken program.
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.
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 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.
Hey I'm having trouble figuring out how to get my template header to work. I have to get my init constructor to take in an array and reverse it. So for example if I have [1,2,3,4] it takes it in [4,3,2,1]
this is my template class:
#pragma once
#include <iostream>
using namespace std;
template<typename DATA_TYPE>
class Reverser
{
private:
// Not sure to make this DATA_TYPE* or just DATA_TYPE
DATA_TYPE Data;
public:
// Init constructor
Reverser(const DATA_TYPE& input, const int & size)
{
// This is where I'm getting my error saying it's a conversion error (int* = int), not sure
// What to make Data then in the private section.
Data = new DATA_TYPE[size];
for(int i=size-1; i>=0; i--)
Data[(size-1)-i] = input[i];
}
DATA_TYPE GetReverse(){
return Data;
}
~Reverser(){
delete[] Data;
}
};
So yea if you could tell me what I'm doing wrong that'd be great.
That's because when you pass array to function it converts to pointer. You must use DATA_TYPE as pointer:
template<typename DATA_TYPE>
class Reverser
{
private:
// Not sure to make this DATA_TYPE* or just DATA_TYPE
DATA_TYPE* Data; //pointer
public:
// Init constructor
Reverser(const DATA_TYPE* input, const int & size) //pointer
{
// This is where I'm getting my error saying it's a conversion error (int* = int), not sure
// What to make Data then in the private section.
Data = new DATA_TYPE[size];
for(int i=size-1; i>=0; i--)
Data[(size-1)-i] = input[i];
}
DATA_TYPE* GetReverse(){ //Returns Pointer
return Data;
}
~Reverser(){
delete[] Data;
}
};
It seems to me like you are declaring an instance of this class with int, like
Reverser<int> myVar;
Then the Data member will be of type int. In the constructor you then try to allocate memory (with new returning a int*) and assign it to the Data member, but you can't assign a pointer to a non-pointer.
So as you write in your comment, it should be
DATA_TYPE* Data;