Using this template class works perfectly fine when main operates with constructed variables of type dlring, yet my goal is to allow dynamic allocation, so I can handle a non-predefined number of doubly linked circular lists to allow usage of such functions as:
- Splitting a list into two by either using a node position (via
iteration) or value entry. - Same goes for linking two lists into one with a single head/tail pair.
- Node exporting from one list (instance) to another.
- Etc.
I'm pretty much sure there is an elegant workaround which is simply not known by me yet, but I don't think it's nice to come up with a question for the community if you didn't struggle enough to resolve. (checked google
So with that goals I'm supposed to dynamically allocate memory (via constructor calls) using some kind of pointer-to-pointer, AFAIK. If there is a smarter way to implement these, please let me know. My solution attempt is given in the end of this snippet. Feel free to criticize all of the below.
Doubly linked circular list class header (simplified)
template <typename T>
class dlring
{
struct node
{
T data;
node* prev;
node* next;
node(T t, node* p, node* n) : data(t), prev(p), next(n) {}
};
node* head;
node* tail;
public:
dlring():head(nullptr), tail(nullptr){}
bool empty() const { return ( !head || !tail ); }
//operator bool() const { return !empty(); }
void Push(T);
T pop_back();
~dlring()
{
while(head)
{
node* temp(head);
head=head->next;
delete temp;
}
}
};
Should I use the commented out operator bool overload?
pop_back and Push methods:
template <typename T>
void dlring<T>::Push(T data)
{
head = new node(data, tail, head);
if( head->next )
{
head->next->prev = head;
tail->next = head;
}
if( empty() )
{
tail = head;
head->next=tail;
head->prev=tail;
tail->next=head;
tail->prev=head;
}
}
template<typename T>
T dlring<T>::pop_back()
{
if( empty() )
std::cout<<"List empty";
node* temp(tail);
T data( tail->data );
tail = tail->prev ;
if (tail != temp)
{
tail->next->next = head;
head->prev = tail;
}
else
{
head = nullptr;
tail = nullptr;
}
delete temp;
temp = nullptr;
return data;
}
My attempt doesn't have the right behaviour: When I'm trying to show all the lists via a iteration the code fails, segfaulting on head->data access attempt of dlist[0], where 0 is an iteration of k. Here is the snippet:
int main()
{
int k;
std::cout<<"Rings count?"<<std::endl;
std::cin>>k;
dlring<int>* dlist = new dlring<int>[k]; //I suppose I'm allocating *k*
//dlring<int> elements. this line is not confirmed to call the constructor.
(dlist[0]).Push(10);
(dlist[0]).Push(13);
(dlist[1]).Push(99);
/*{
while(!dlist[0].empty())
std::cout<<(dlist[0]).pop_back()<<" ";
std::cout<<std::endl;
while(!dlist[1].empty())
std::cout<<(dlist[1]).pop_back()<<" ";
}*/
//this section works perfectly fine, while this
for(int i=0;i<k;i++)
{
while(!dlist[k].empty())
std::cout<<(dlist[k]).pop_back()<<" ";
std::cout<<std::endl;
}
//is causing a segmentation fault while attempting to access dlist[*0*].tail->data.
std::cout<<(dlist[0]).head->data;
//line was checked and is confirmed to be functional,
//I suppose dlist[variable] has some trick I don't know yet.
//what I wish to look like an instance call would be *
return 0;
}
Best regards. Again, feel free to criticize any of my code/logics.