Destructor of a class C with members of pointer C - c++

Assume the following class declaration:
class NTree
{
private:
const T* fKey;
NTree<T, N>* fNodes[N]; // N subtrees of degree N
NTree();
...
}
in which we can add some fNodes, representing a subtree given an index. These will be dynamically allocated using new. However, there are elements which are static, and not dynamically allocated:
public:
static NTree<T, N> NIL; // sentinel
...
We choose to allocate this on the stack using the default constructor supplied above.
template<class T, int N>
NTree<T, N> NTree<T, N>::NIL;
Now, let's say we wish to delete an NTree. The class NTree is recursive, having pointer to NTree inside it.
This is what I'm struggling with.
I understand the logic behind a destructor, if we have e.g.
class MyClass
{
private:
TypeA * myA;
TypeB * myB;
TypeC * myC;
...
}
We could use a destructor to prevent these pointers from dangling, or getting lost.
~MyClass()
{
delete myA;
delete myB;
delete myC;
}
However, when it comes to a recursive class, I have no clue how to wrap my mind around this, how to understand deletion.
A simple thing to think of:
template<class T, int N>
NTree<T, N>::~NTree()
{
delete[] fNodes;
}
However, it won't work, as some nodes are NIL (stack allocated), so deleting them will result in a crash.
Another idea is:
template<class T, int N>
NTree<T, N>::~NTree()
{
for (int i = 0; i < N; i++)
{
delete fNodes[i];
}
}
However, this will result in a stack overflow, because of the stack being bombarded with frames for each recursive call of ~NTree()
And the following:
template<class T, int N>
NTree<T, N>::~NTree()
{
for (int i = 0; i < N; i++)
{
if (fNodes[i] != &NIL)
delete fNodes[i];
}
}
Results in a read exception, because the recursive calls will deallocate fNodes[i] for a particular stack frame, thus trying to access that memory is invalid.
So my question is, how can I delete a member variable, where that member is recursively defined as the same class?
How can I make my destructor work?
Edit: Attempt to provide more information without making it too convoluted
I'm defining a destructor so it's probably wise to show you my copy constructor and assignment operator:
template<class T, int N>
NTree<T, N> & NTree<T, N>::operator=(const NTree & aOtherNTree)
{
//This is an already initialized object.
if (this != &aOtherNTree)
{
fKey = aOtherNTree.fKey;
for (int i = 0; i < N; i++)
{
if (fNodes[i] == &NIL)
continue; //continue if nil
delete fNodes[i]; //important -- so no dangling pointer
fNodes[i] = new NTree<T, N>; //allocate memory
fNodes[i] = aOtherNTree.fNodes[i]; //assign
}
}
return *this;
}
..
template<class T, int N>
NTree<T, N>::NTree(const NTree & aOtherNTree)
{
//This is a new object, nothing is initalized yet.
fKey = aOtherNTree.fKey;
for (int i = 0; i < N; i++)
{
if (fNodes[i] == &NIL)
continue;
fNodes[i] = new NTree<T, N>;
fNodes[i] = aOtherNTree.fNodes[i];
}
}
I hope this shows all instances of when I allocate memory that needs explicit deletion in the destructor.
NIL is a sentinel, we always assign a leaf to NIL.
This part is provided by the professor, it is where we set up the initial objects:
NS3Tree root(A);
root.attachNTree(0, *(new NS3Tree(A1)));
root.attachNTree(1, *(new NS3Tree(A2)));
root.attachNTree(2, *(new NS3Tree(A3)));
root[0].attachNTree(0, *(new NS3Tree(AA1)));
root[1].attachNTree(0, *(new NS3Tree(AB1)));
root[1].attachNTree(1, *(new NS3Tree(AB2)));
A1, A2, etc, are strings

Your copy constructor and assignment operator are both totally wrong.
if (fNodes[i] == &NIL)
continue; //continue if nil
delete fNodes[i]; //important -- so no dangling pointer
This is wrong logic. If your old child value was NIL, it will stay NIL forever, because it will be never assigned. This should be:
if (fNodes[i] != &NIL)
delete fnodes[i];
Of course in the copy ctor the above fragment should not appear, because fNodes[i] doesn't have any determined value. It should only appear in the assignment.
Now
fNodes[i] = new NTree<T, N>; //allocate memory
fNodes[i] = aOtherNTree.fNodes[i]; //assign
You allocate some node and then immediately overwrite a pointer to it with another pointer, managed by another node. The first assignment thus has no effect, except for a memory leak. The second one will result in an error later on. Here's a correct invocation
if (aOtherNTree.fNodes[i] == &NIL)
fNodes[i] = &NIL;
else
fNodes[i] = new NTree<T, N> (*aOtherNTree.fNodes[i]); // make a new copy
An alternative else clause is
else {
fNodes[i] = new NTree<T, N>;
*fNodes[i] = *aOtherNTree.fNodes[i]); // assign the object, not the pointer
}
I recommend writing a debugging function that would print a tree, including the address of each node. While debugging, print every tree you make to ensure no pointer sharing occurs.

Related

Resize array using new

So I am trying to resize an array by making a function call ResizeArray(). However, I don't know what is the correct way to use "delete" in this case. (I make a new int * and copy the value from the original to it and then I make the original pointer points to the new one, now I don't know what to "delete"
class Base
{
private:
int sizeInClass;
int *arrayy=nullptr;
public:
Base(int s)
{
sizeInClass=s;
arrayy = new int[s]{};
setValue();
};
void setValue()
{
for(int x=0;x<sizeInClass;x++)
{
arrayy[x]=x;
}
}
void print()
{
int countter=0;
for(int x=0;x<sizeInClass;x++)
{
countter++;
cout<<arrayy[x]<<endl;
}
cout<<"The size of the array is : "<<countter<<endl;
}
void ResizeArray(int newSize)
{
int *newArray = nullptr;
newArray = new int[newSize];
for(int x=0;x<sizeInClass;x++)
{
newArray[x]=arrayy[x];
}
delete [] arrayy; /////////////////////////////////// should i use deleate here ?
arrayy = newArray;
delete [] newArray; /////////////////////////////////// or should I use deleate here ?
sizeInClass = newSize;
}
~Base()
{
delete [] arrayy; /////////////////////////////////// or just use delete here
arrayy=nullptr;
}
};
int main()
{
Base b(5);
b.print();
b.ResizeArray(8);
b.setValue();
b.print();
return 0;
}
The first and the 3rd of your suggested delete are correct.
Regarding handling resources,
for sure you need de-allocation in destructor, to free resources when your container class
is destroyed. When you want to resize contained array, you are handling it in ResizeArray function, so below is basic proposal for it, with clarification comments:
void ResizeArray(int newSize)
{
int *newArray = new int[newSize];
if (nullptr != newArray) { // we take action only if allocation was successful
for(int x=0;x<sizeInClass;x++)
{
newArray[x]=arrayy[x];
}
delete [] arrayy; // good, here you delete/free resources allocate previously for an old array
arrayy = newArray; // good, you redirect member ptr to newly allocated memory
/* delete [] newArray; ups, we have member ptr point to this location
and we cannot delete it, after this, accessing it would be UB,
beside in dtor we would have double, second deletion */
sizeInClass = newSize;
}
}
Your destructor is fine.
There could be further improvements in your code, but this is related to your question.

C++: Copy constructor crashing

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.

C++ -- determine if generic variable is a pointer

I have a class, which I'll refer to as myclass, that has a list container of the type T. I also have a couple of methods that remove items from the list. Should the T be a pointer of some sort, I would like to check that it indeed is a pointer and then delete it in order to relieve allocated resources back to memory. Here's a snippet of code:
template<typename T>
class myclass{
private:
std::list<T> * container;
// other vars
public:
void erase(const T &item){
if (!this->find(item)) // find is defined elsewhere
return false;
auto temp = container->begin();
for (int i = 0; i < container->size(); ++i){
// this is where i would like to check if *temp is a pointer,
// so that I can assign it to a pointer var, remove it from the list,
// then delete the pointer,
//otherwise just simply remove it from the list.
}
}
};
EDIT
auto temp = container->begin();
I want to know how to determine if *temp is a pointer so that I can do the following:
T * var = *temp;
container->remove(temp); // remove or erase, i can't recall at the moment
delete var;
but I only want to do that if *temp is a pointer
1) Determine if Type is a pointer in a template function
2) How would you know if that pointer is pointing to dynamically allocated memory?
I don't think this is a wise idea. You don't know whether the user has provided pointers to data allocated on the stack, or to data that is managed in some other way (eg with smart pointers).
But to answer the question, look at
std::is_pointer<T>::value // in type_traits header
http://en.cppreference.com/w/cpp/types/is_pointer
This is a C++11 feature.
Sorry, but no: std::list<T>::iterator (which is what begin() will return and therefore will be the type of temp) can't ever be a pointer. It must be a type that (at the very least) overloads pre- and post-increment and decrement to do linked list traversal so ++ will do something like pos = pos->next; and -- will to something like pos = pos->prev;.
If you're trying to figure out if *temp (which will be the same type as T) is a pointer, that's a whole different story. You basically have two routes. The one I'd prefer as a general rule would be to provide a specialization of your class for pointers:
template<typename T>
class myclass{
private:
std::list<T> container;
// other vars
public:
void erase(const T &item){
if (!container->find(item)) // find is defined elsewhere
return false;
auto temp = container->begin();
for (int i = 0; i < container->size(); ++i){
container.erase(temp);
}
}
};
template<class T>
class myclass <T *> {
private:
std::list<T> container;
// other vars
public:
void erase(const T &item){
if (!container->find(item)) // find is defined elsewhere
return false;
auto temp = container->begin();
for (int i = 0; i < container->size(); ++i){
delete *temp;
container.erase(temp);
}
}
};
The biggest problem with this is that you may end up duplicating a fair amount between the base template and the specialization for pointers. There are a couple of ways of avoiding that. One is to use a base class that implements the common behavior, then derive the two specializations from that to provide the specialized behavior. Another would be to use some enable_if or SFINAE to enable different versions of the erase function depending on whether the contained type is something that can be dereferenced or not.
As an aside, you probably shouldn't have std::list<T> *container; -- it should probably be just std::list<T> container; (or, better still in most cases, std::vector<T> container;)
Isn't it annoying container's don't delete normal pointers? Well in C++ raw pointers don't actually own the object. There could be many pointers pointing to the same object. You need a unique pointer - stl provides one in c++11. When a unique_ptr is removed from the list, it will destroy the object it points to, so there is no need to complicate erase.
#include <list>
#include <memory>
#include <type_traits>
using namespace std;
template<typename t, bool b>
struct Selector {
typedef list<T> container;
};
template<typename t>
struct Selector<t, true> {
typedef list<unique_ptr<T> > container;
};
template<typename T>
class myclass{
private:
Selector<T, is_pointer<T>::value>::container* container;
// other vars
public:
void erase(const T &item){
if (!this->find(item)) // find is defined elsewhere
return false;
auto temp = container->begin();
for (int i = 0; i < container->size(); ++i){
// removing the unique_ptr delete's pointer
}
}
};

Generic list deleting non pointers

I have a generic list with a template
template<class t>
class GenericList {
//the data is storeed in a chained list, this is not really important.
struct c_list { t data; c_list* next; ...constructor... };
public:
bool isDelete;
GenericList() : isDelete(false) {...}
void add(t d) {
c_list* tmp = new c_list(d, first->next);
//this is not really important again...
}
~GenericList() {
c_list* tmp = first;
c_list* tmp2;
while(tmp->next!=NULL) {
if (isDelete) { delete tmp->data; } //important part
tmp2=tmp->next;
delete tmp;
tmp=tmp2;
}
}
};
The important part is the isDelete
This is only a sample code
I need this because I want to store data like this:
GenericList<int> list;
list.add(22);list.add(33);
and also
GenericList<string*> list;
list.add(new string("asd")); list.add(new string("watta"));
The problem if I store only <int> the compiler said that I cannot delete non pointer variables, but I don't want to in this case. How can I solve this?
when I store <int*> there is no compiler error...
Without changing much your code, I would solve your problem as
template<class t>
class GenericList
{
//same as before
//add this function template
template<typename T>
void delete_if_pointer(T & item) {} //do nothing: item is not pointer
template<typename T>
void delete_if_pointer(T* item) { delete item; } //delete: item is pointer
~GenericList() {
c_list* tmp = first;
c_list* tmp2;
while(tmp->next!=NULL) {
delete_if_pointer(tmp->data); // call the function template
tmp2=tmp->next;
delete tmp;
tmp=tmp2;
}
}
};
EDIT: I just noticed that #ildjarn has provided similar solution. However there is one interesting difference: my solution does NOT require you to mention the type of data when calling the function template; the compiler automatically deduces it. #ildjarn's solution, however, requires you to mention the type explicitly; the compiler cannot deduce the type in his solution.
I would create a nested struct template inside your class to help:
template<typename U>
struct deleter
{
static void invoke(U const&) { }
};
template<typename U>
struct deleter<U*>
{
static void invoke(U* const ptr) { delete ptr; }
};
Then change the line that was using isDelete from
if (isDelete) { delete tmp->data; }
to
if (isDelete) { deleter<t>::invoke(tmp->data); }
delete on an int makes a program ill-formed, so the compiler will reject it, even though the delete would never be reached.
What you want is only possible if you switch from "bare" pointers to smart pointers such as unique_ptr or shared_ptr; those handle memory management for you, without explicit delete.

How to delete a template?

I'm having trouble with deleting my template.
My template and destructor:
template<class S, class T>
class Consortium
{
private :
map<const S, Node<T>*> m_consortiumMap;
Heap<T>m_consortiumHeap;
public :
~Consortium();
void Insert(const S key, T toAdd);
void Update(const S key);
void Remove(const S key);
const T Top();
};
template<class S, class T>
Consortium<S,T>::~Consortium()
{
m_consortiumMap.clear();
delete &m_consortiumHeap.;
}
My heap and destructor:
template <class T>
class Heap
{
private :
vector<Node<T>*> m_heapVector;
public :
~Heap();
int parent(int i) const {return i / 2;}
int left(int i) const {return 2 * i;}
int right(int i) const {return 2 * i + 1;}
void heapify(int index);
Node<T>* extractMin ();
void heapDecreaseKey (int index, Node<T>* key);
void MinHeapInsert (Node<T>* key);
Node<T>* ExtractNode(int index);
Node<T>* top ()const {return m_heapVector[0];}
};
template<class T>
Heap<T>::~Heap()
{
for (int i = 0 ; i < m_heapVector.size() ; i++)
m_heapVector.erase(m_heapVector.begin() + i);
}
and this is the object that holds the template, I'm having problems with that also:
class Garage
{
private :
Consortium<string, Vehicle*> m_consortium;
public :
~Garage() {delete &m_consortium;}
};
what's wrong here?
This is wrong on its face:
delete &m_consortiumHeap;
You must only delete things that you allocated with new. m_consortiumHeap is part of the class and gets automatically allocated when the class gets allocated and automatically deallocated when the class gets deallocated. You cannot and must not explicitly delete it.
This may have the opposite problem:
m_consortiumMap.clear();
the contents of m_consortiumMap are pointers. I can't tell from the code you've shown, but if the nodes within the map are allocated by the Consortium class using new, they must be deleteed, otherwise you will leak memory. Clearing the map will only get rid of the pointers, it will not deallocate the memory that they point to. You must first iterate through the map and delete each element. While deallocation of the elements is important, clearing the map in the destructor is kind of pointless since the map itself will be destroyed immediately afterwards anyway.
This is just perplexing:
for (int i = 0 ; i < m_heapVector.size() ; i++)
m_heapVector.erase(m_heapVector.begin() + i);
first of all, everything I said about m_consortiumMap applies also to m_heapVector: If the contents were allocated with new by the Heap class, you must delete them in the destructor. And erasing the pointers from the vector is pointless, not to mention that the above loop has a logic error in it. When you iterate over a container, you should use the iterators themselves, e.g.
for (std::vector<Node<T>*>::iterator i = m_heapVector.begin() ; i != m_heapVector.end() ; i++)
Also, std::vector, like std::map, has a clear() function, but like I said it's pointless to clear the vector in the destructor. What you really want to do is deallocate the elements (if necessary).
If you didn't use new to create an object you can't use delete to get rid of it.
You probably want to delete the objects pointed by the elements in the vector. Erase method doesn't do that, it just remove the pointer element from the vector, without destroying the pointed object. So you need (I presume) to delete the pointed object first, to avoid memory leaks. You cold do this:
for( vector<Node<T>*>::iterator iter = m_heapVector.begin(), endI = m_heapVector.end(); iter != endI; ++iter)
{
delete *iter;
}
// m_heapVector.clean(); // Not necessary in destructor, since the vector will be destroyed anyway.
Using C++0x functions:
std::for_each( m_heapVector.begin(), m_heapVector.end(), []( Node<T>* node) { delete node; });
Also, use clear() method of the container (vector in your case) to remove all the elements.
Since m_consortiumHeap is a data member of your class (directly, not a pointer to it), you don't have to explicitly delete it. When the Consortium instance is destructed, it will automatically call the destructor of m_consortiumHeap for you.