overloading operator=, troublesome destructor in a class C++ - c++

I am trying to implement a one-directional list. Everything works perfectly fine until the command m3=m1+m2 is doubled in the main function. When I was debugging I noticed that in the overloaded = operator, after the destruction happens, the values assigned to the o1 object disappear. No idea if there is something wrong with destructor, or the operator =.
Here is the code:
#include <iostream>
using namespace std;
template <class T>
class Element;
template <class T>
class List{
friend class Element<T>;
Element<T> *head;
public:
List(){
cout<<"konstruktor"<<endl;
head=NULL;
}
~List() {
Element<T> *tmp = head;
cout << "destruktor" << endl;
while (tmp) {
//tmp = tmp->next;
delete head;
head = tmp;
}
}
friend istream &operator>>(istream &p, List<T> &o1){
Element<T>* new_ele;
Element<T>* it;
it=o1.head;
new_ele=new Element<T>;
p>>new_ele->value;
new_ele->next=NULL;
if (o1.head==NULL){
o1.head=new_ele;
}
else{
while (it->next!=NULL){
it=it->next;
}
it->next=new_ele;
}
return p;
}
friend ostream &operator<<(ostream &s, List<T> &o1){
Element<T>* it;
it=o1.head;
while(it){
s<<it->value<<" ";
it=it->next;
}
return s;
}
List <T> &operator=(const List<T> &o1){
if (this==&o1){
return *this;
}
Element<T> *it1, *it2, *itc;
this->~List();//this is the where everything goes haywire
itc=head;
it1=o1.head;
while(it1){
itc=new Element<T>;
if (!head) head=it1;
itc->next=NULL;
itc->value=it1->value;
it1=it1->next;
itc=itc->next;
}
return *this;
}
List<T> &operator+(List<T> &o1){
if(o1.head==NULL){
return *this;
}else if(head==NULL){
return o1;
}
static List<T> res=*this;
Element<T> *it;
it=res.head;
while(it->next) {
it = it->next;
}
Element <T> *o1_it=o1.head;
while(o1_it){
Element<T> *copy;
copy=new Element<T>;
copy->next=NULL;
copy->value=o1_it->value;
it->next=copy;
it=it->next;
o1_it=o1_it->next;
}
return res;
}
int length_list(){
Element<T> *it;
int count_elements=0;
it=head;
while(it->next){
count_elements++;
it=it->next;
}
return count_elements;
}
void bubblesort_List(){
Element<T> *it;
for(int i=0; this->length_list() > i;i++){
it = head;
while (it->next) {
if (it->next->value < it->value) {
T tmp = it->value;
it->value = it->next->value;
it->next->value = tmp;
}
it = it->next;
}
}
}
};
template <class T>
class Element{
friend class List<T>;
friend istream &operator>>(istream &p, List<T> &o1);
friend ostream &operator<<(ostream &s, List<T> &o1);
Element<T> *next;
T value;
public:
Element(){
next=NULL;
}
};
int main(){
List<int> m1, m2, m3;
cin>>m1>>m1>>m1;
cin>>m2;
m3=m1+m2;
m3=m1+m2;
cout<<m3<<endl;
return 0;
}

this->~List();
this destroys the object. It doesn't just run the destructor.
After an object is destroyed, its storage remains, but there is no object there. Interacting with the storage as if there was an object there is undefined behavior.
Move the body of the destructor to a helper function called clear():
Fix it:
clear() {
std::cout << "clear" << std::endl;
while (head) {
Element<T>* tmp = head;
head=head->next;
delete tmp;
}
}
and make your ~List() be simply clear().
Next, lets fix your operator=.
List& operator=(List&&o) {
if (this==&o)
return *this;
clear();
head = o.head;
o.head = nullptr;
return *this
}
List& operator=(const List &o) {
if (this== &o)
return;
*this = List(o); // call operator=(List&&) using a copy of o
return *this;
}
List(List&& o):
head(o.head)
{
o.head = nullptr;
}
List():head(nullptr) {}
// all of the work goes on here:
List(List const& o):List()
{
Element<T>* src = o.head;
Element<T>** dest = &head;
while(src) {
*dest = new Element<T>;
(*dest)->value = src->value;
(*dest)->next = nullptr;
src = src->next;
dest = &((*dest)->next);
}
}
I have chained everything to work off simpler functions.
=(&&) and (&&) (move-assign and move-construct) copies head and clears the source. This is a small amount of duplicate code.
=(const&) (copy-assign) uses =(&&) (move-assign) and (const&) (copy-construct). The business of copying nodes is hard; do it in one situation. Copying into an empty instance is easier than into one that already exists.
(const&) is the only one that does the work. Here I keep a pointer-to-the-tail-pointer of my linked list, and a pointer-to-the-next-element-to-add.
Then I splice a copy of the next element in, and update my tail and next element pointers.

Two main issues
call delete to invoke destructor, never explicitly call ~List. Just call it free_list or something and call it on head
your destructor is broken, it's not freeing memory up correctly
you want :
void free_list (Element<T> * head) {
Element<T> *tmp = head;
while (tmp != null) {
head = tmp.next;
delete tmp;
tmp = head;
}
}

Related

Overloading operator ++ for links in a linked list (c++)

Consider a standard implementation of class Link of LinkedList in c++.
I want to know if its a good idea to overload the operator ++ in this class (I noticed that I repeat the line link = link->next; a lot when dealing with linked lists, so I thought it would be easier if I overload this operator). Here's my implementation:
#ifndef LINK_H
#define LINK_H
#include <iostream>
#include "typeinfo.h"
#include "LinkedList.h"
template <class T> class LinkedList; //|Forward declaration of the generic LinkedList.
template <class T>
class Link
{
public:
//|-------------------- Constructors --------------------
Link(T data): m_data(data), next(NULL){}
//|-------------------- Methods --------------------
T getData(){
return m_data;
}
T& getNext(){
return next;
}
void setNext(Link* newLink){
next = newLink;
}
void setData(T data){
m_data = data;
}
//|-------------------- Operator overload --------------------
bool operator==(Link& other){
if(this->m_data == other.m_data)
return true;
return false;
}
void operator++(){ //Is this okay?
this = this->next;
}
//|-------------------- Friend functions --------------------
friend std::ostream& operator<<(std::ostream& out,const Link<T>& link){
out<<link.m_data;
return out;
}
//|-------------------- Destructor --------------------
virtual ~Link(){}
protected:
public:
//|-------------------- Private fields --------------------
T m_data;
Link<T>* next;
friend class LinkedList<T>;
};
#endif // LINK_H
I guess the way that I tried to do it is not good (it does work as I expected). I tried to use this because I want it to work on pointer that is pointing to a certain link.
So, is it a good idea? if it is, what is the right way to implement it?
Thanks.
Maybe you should refactor your design and the code.
The link, or better said the Node, is normally implemented as an own class. An this class is embedded in the LinkedList class. And that Node class should be completely encapsulated and not shown to the outside world.
The user of the class will just deal with the data value. All the pointer stuff should be hidden.
And to be able to iterate over your class, which is similar to a std::forward_list, you can add ultra simple iterator functionality. Most functions can be implemented using a one liner.
I will show you below an ultra simple implementation example. This may be extended easily according to your needs.
From that you may take over some ideas to improve your design.
With the added iterator functionality, you may use range based for loops and std:: algorithms and all the like.
Please have a look and ask questions, if there should be something unclear.
#include <iostream>
#include <iterator>
#include <initializer_list>
#include <algorithm>
// Very simple implementation of a forward list
template <class T>
class SinglyLinkedList {
// The node
struct Node {
T data{}; // Data. Would normally be a templated argument
Node* next{}; // And the pointer to the next node
Node(const T& i, Node* n = nullptr) : data(i), next(n) {}; // Simple constructor to set a value and next pointer
};
Node* head{}; // This is the start of the list
// It would be advisable to have a tail pointer. We use the more inefficient approach here
Node* getLast() const { Node* n{ head }; while (n and n->next) n = n->next; return n; }
public:
// Constructor / Destructor --------------------------------------------------------------------------------------------------------
~SinglyLinkedList() { clear(); }
// Default constuctor
SinglyLinkedList() {} // Default
// From an initialization list
SinglyLinkedList(const std::initializer_list<T>& il) { clear(); for (const T& i : il) push_back(i); } // From initializer list
// Copy constructor
SinglyLinkedList(const SinglyLinkedList& other) { clear(); for (const T &i : other) push_back(i); }
// Move constructor. Will steal the elements from the other
SinglyLinkedList(SinglyLinkedList&& other) noexcept { head = other.head; other.head = nullptr; }
// Assignment operator
SinglyLinkedList& operator = (const SinglyLinkedList& other) { clear(); for (const T &i : other) push_back(i); }
// Move assignment operator
SinglyLinkedList& operator = (SinglyLinkedList&& other) { head = other.head; other.head = nullptr; }
// Housekeeping --------------------------------------------------------------------------------------------------------------
void clear() { Node* tmp{ head }; while (tmp) { Node* toDelete{ tmp }; tmp = tmp->next; delete toDelete; } head = nullptr; }
int empty() const { return head == nullptr; }
int size() const { int k{}; Node* n{ head }; while (n) { ++k; n = n->next; } return k; }
// Modify content --------------------------------------------------------------------------------------------------------------
void push_front(const T& i) { Node* n = new Node(i); n->next = head; head = n; };
void push_back(const T& i) { Node* n = new Node(i); Node* l = getLast(); if (l) l->next = n; else head = n; }
void pop_front() { if (head) { Node* tmp = head->next; delete head; head = tmp; } }
void pop_back() { // This is a little bit more difficult in a singly linked list
if (head) {
Node* n{ head }, * previous{};
while (n and n->next) {
previous = n;
n = n->next;
}
delete n;
if (previous)
previous->next = nullptr;
else
head->next = nullptr;
}
}
// Access elements --------------------------------------------------------------------------------
T front() const { return head ? head->data : 0; };
T back() const { Node* n = getLast(); return n ? n->data : 0; }
// Add iterator properties to class ---------------------------------------------------------------
struct iterator { // Local class for iterator
Node* iter{}; // Iterator is basically a pointer to the node
// Define alias names necessary for the iterator functionality
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
// Constructor
iterator() {}
iterator(Node* n) : iter(n) {}
// Dereferencing
reference operator *() const { return iter->data; }
pointer operator ->() const { return &iter->data; }
// Aithmetic operations
iterator& operator ++() { if (iter) iter = iter->next; return *this; }
iterator operator ++(int) { iterator temp{ *this }; ++* this; return temp; }
iterator operator +(const difference_type& n) const { iterator temp{ *this }; difference_type k{ n }; while (k--)++temp; return temp; }
iterator& operator +=(const difference_type& n) { difference_type k{ n }; while (k--)++* this; return *this; };
// Comparison
bool operator != (const iterator& other) const { return iter != other.iter; }
bool operator == (const iterator& other) const { return iter == other.iter; }
bool operator < (const iterator& other) const { return iter < other.iter; }
bool operator > (const iterator& other) const { return iter > other.iter; }
bool operator <= (const iterator& other) const { return iter <= other.iter; }
bool operator >= (const iterator& other) const { return iter >= other.iter; }
// Difference. Also complicated, because no random access
difference_type operator-(const iterator& other) const {
difference_type result{};
Node* n{ iter };
while (n and n != other.iter) {
++result;
n = n->next;
}
return result;
}
};
// Begin and end function to initialize an iterator
iterator begin() const { return iterator(head); }
iterator end() const { return iterator(); }
// Functions typcical for forward lists ----------------------------------------------------------------------
// Easy, becuase we can operate form the current iterator and do not need the "previous" element
iterator insertAfter(iterator& pos, const T& i) {
iterator result{};
if (pos.iter and pos.iter->next) {
Node* n = new Node(i, pos.iter->next);
pos.iter->next = n;
result = n;
}
return result;
}
iterator eraseAfter(iterator& pos) {
iterator result{};
if (pos.iter and pos.iter->next) {
Node* tmp = pos.iter->next->next;
delete pos.iter->next;
pos.iter->next = tmp;
result = pos.iter->next;
}
return result;
}
};
// Test/Driver Code
int main() {
// Example for initilizer list
SinglyLinkedList<int> sllbase{ 5,6,7,8,9,10,11,12,13,14,15 };
// Show move constructor
SinglyLinkedList<int> sll(std::move(sllbase));
// Add some values in the front
sll.push_front(4);
sll.push_front(3);
sll.push_front(2);
sll.push_front(1);
// Delete 1st element (Number 1)
sll.pop_front();
// Delete last element
sll.pop_back();
// Use a std::algorithm on our custom linked list. Works because we have an interator
SinglyLinkedList<int>::iterator iter = std::find(sll.begin(), sll.end(), 8);
// Now add an element after 8
iter = sll.insertAfter(iter, 88);
// End delete the 9
iter = sll.eraseAfter(iter);
// Use range based for loop. Works because, we have iterators
for (int i : sll)
std::cout << i << ' ';
}

Linked list consist of a class throws exception 0xC0000005

So i have a Linked list implementation of my own and it can successfully keep integers and call them when needed with overloaded [] operator but when it comes to storing a class in my linked list, it seems that i can't call the class appropriately (using the same [] operator).
Called functions and members of my Linked List;
#include <iostream>
#include <assert.h>
template<typename T>
struct node {
T data;
node<T>* next;
};
template<typename T>
class Vectem {
private:
node<T>* head;
node<T>* last;
int lenght;
public:
void insert(T value) {
last->next = new node<T>;
last = last->next;
last->data = value;
last->next = NULL;
if (isEmpty()) {
head = last;
}
lenght++;
}
node<T>* search(int indx) {
node<T>* current;
current = head;
int count=0;
while (current != NULL) {
if (count == indx) {
break;
}
current = current->next;
count++;
}
return current;
}
T& operator [](int indx) {
assert(indx >= lenght - 1);
T result;
result = search(indx)->data;
return result;
}
};
And here is the main function and the class that i try to store;
#include <iostream>
#include <fstream>
#include <string>
#include "VectemLibrary.h"
class word {
public:
std::string value;
int count;
word(std::string value, int count): value(value),count(count) {
}
word() {
value = "NOT ASSIGNED";
count = 0;
}
word(const word& w1) {
value = w1.value;
count = w1.count;
}
~word() {
std::cout << "Word Destroyed" << std::endl;
}
};
int main()
{
Vectem<word> wordContainer;
word newWord("hello", 1);
wordContainer.insert(newWord);
std::cout << wordContainer[0].value;
}
Visual studio gave me the expection with this message at the last line where i call the first member of linked list with [];
Exception thrown at 0x7A0CF3BE (ucrtbased.dll) in Top 10 words.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.
I think that my lack of experience with pointers may have caused the problem but if you see something that i can't, Please enlighten me.
There are other problems with the code you posted as well (e.g. isEmpty() is not declared or defined), but I'll focus on the issue you explicitly mentioned.
In your operator:
T& operator [](int indx) {
assert(indx >= lenght - 1);
// You declare this variable on the stack
T result;
result = search(indx)->data;
// And then you return this variable by reference; this is not okay
return result;
}
As mentioned in my code comments (and by #Johnny Mopp in his comment to your post), you shouldn't (can't) return a reference or pointer to a variable declared within the returning function and constructed on the stack. Anything on the stack will be destroyed once the function call ends, so any returned pointers or references to such variables will be dangling references; using said pointers or references will result in undefined behavior.
So you don't want to return a reference to a stack-allocated variable like result; you want to return a reference to the data within the node itself (which is allocated on the heap by insert()), as it will still be a valid reference after the function returns:
return search(indx)->data;
There are several problems with your code, but the most important is that you are not initializing the head, last, or lenght members of Vectem at all. An Access Violation error at address 0xCCCCCCCC is a good indication that uninitialized memory is being accessed, as some compilers/setups fill uninitialized memory with 0xCC bytes, thus head and last are initially 0xCCCCCCCC in your case.
You need to add appropriate constructors to Vectem (as well as a destructor, a copy constructor, and a copy assignment operator, per the Rule of 3), eg:
template<typename T>
class Vectem {
private:
node<T>* head;
node<T>* last;
int lenght;
public:
Vectem() : head(NULL), last(NULL), lenght(0) {}
Vectem(const Vectem &src) : head(NULL), last(NULL), lenght(0)
{
// copy src's data to *this as needed ...
}
~Vectem()
{
// cleanup *this as needed ...
}
Vectem& operator=(const Vectem &rhs)
{
if (&rhs != this) {
// clear *this, and copy rhs's data to *this, as needed ...
}
return *this;
}
...
};
Or, in C++11 and later, you can initialize the members directly in their declarations (also, be sure to add a move constructor and a move assignment operator, per the Rule of 5), eg:
template<typename T>
class Vectem {
private:
node<T>* head = nullptr;
node<T>* last = nullptr;
int lenght = 0;
public:
Vectem() = default;
Vectem(const Vectem &src)
{
// copy src's data to *this as needed ...
}
Vectem(Vectem &&src) : head(src.head), last(src.last), lenght(src.lenght)
{
src.head = nullptr;
src.last = nullptr;
src.lenght = 0;
}
~Vectem()
{
// cleanup *this as needed ...
}
Vectem& operator=(const Vectem &rhs)
{
if (&rhs != this) {
// clear *this, and copy rhs's data to *this, as needed ...
}
return *this;
}
Vectem& operator=(Vectem &&rhs)
{
// clear *this as needed...
head = rhs.head; rhs.head = nullptr;
last = rhs.last; rhs.last = nullptr;
lenght = rhs.lenght; rhs.lenght = 0;
return *this;
}
...
};
That being said, insert() is also buggy, as it is dereferencing last before checking that last is actually pointing at a valid node. Try something more like this instead:
void insert(T value) {
node<T> *n = new node<T>{value, NULL};
if (!head) head = n;
if (last) last->next = n;
last = n;
++lenght;
}
Alternatively:
void insert(T value) {
node<T> **p = (last) ? &(last->next) : &head;
*p = new node<T>{value, NULL};
last = *p;
++lenght;
}

What i am making wrong with operator overloading?

I don't understand what is wrong when I overload operator +
(purpose of this is to join 2 stacks in one new) ...
it returns "sum" of but change values for those previous.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T>
classStack
{
private:
struct Node
{
T data;
Node *next;
} *top;
std::size_t size;
public:
Stack();
~Stack();
void push(T data);
void pop(void);
size_t get_size(void);
const Stack& operator=(const Stack &stack_obj);
const Stack operator+(const Stack &stack_obj);
void show_all_stack(void);
};
template <typename T>
const Stack<T> Stack<T>::operator+(const Stack &stack_obj)
{
Stack stack;
Node *tmp;
if (!this->size && !stack_obj.size) {
return stack;
}
if (!stack_obj.size)
{
stack.size = size;
stack.top = top;
}
else if (!size)
{
stack.size = stack_obj.size;
stack.top = stack_obj.top;
}
else
{
stack.size = size + stack_obj.size;
stack.top = new Node;
stack.top = top;
tmp = stack.top;
while (tmp->next)
tmp = tmp->next;
tmp->next = new Node;
tmp->next = stack_obj.top;
}
return stack;
}
Default constructor
template <typename T>
Stack<T>::Stack(void): top(nullptr), size(0)
{
}
Destructor
template <typename T>
Stack<T>::~Stack(void)
{
Node *next;
if (!size)
std::cout << "Stack is empty!\n";
else
{
while (top != nullptr)
{
next = top->next;
delete top;
top = next;
}
top = nullptr;
}
}
Assignment operator
template <typename T>
const Stack<T>& Stack<T>::operator=(const Stack<T> &stack_obj)
{
Node *tmp;
Node *ptr;
Node *last;
Node *new_node;
if (&stack_obj != this)
{
while (top != nullptr)
{
tmp = top;
top = top->next;
delete tmp;
}
top = nullptr;
size = stack_obj.size;
ptr = stack_obj.top;
while (ptr)
{
new_node = new Node;
new_node->data = ptr->data;
new_node->next = nullptr;
if (!top)
{
top = new_node;
last = new_node;
}
else
{
last->next = new_node;
last = new_node;
}
ptr = ptr->next;
}
}
}
Before creating functions that return Stack<T> by value, or have functions that require copy semantics for Stack<T> (as in your operator +), you need to make sure that Stack<T> is safely copyable. Right now, you are lacking a copy constructor, thus your operator + would never work correctly, even if the function itself is bug-free.
You're missing this function:
Stack::Stack(const Stack<T>&)
The easiest way to implement the copy constructor is to take most of your code out of the assignment operator, and place that into the copy constructor. More or less, the code should look something like this (the code has not been compiled or tested -- it is there to illustrate the main point of this answer):
template <typename T>
Stack<T>::Stack<T>(const Stack<T> &stack_obj) : top(nullptr), size(0)
{
Node *tmp;
Node *ptr;
Node *last;
Node *new_node;
ptr = stack_obj.top;
while (ptr)
{
new_node = new Node;
new_node->data = ptr->data;
new_node->next = nullptr;
if (!top)
{
top = new_node;
last = new_node;
}
else
{
last->next = new_node;
last = new_node;
}
ptr = ptr->next;
}
}
You have already written a destructor that should work, so we won't get into that function, but a working destructor is required for the next step -- implementation of the assignment operator.
Now that you have a copy constructor and destructor, you can now write the assignment operator. The technique used here is the copy / swap idiom
#include <algorithm>
//...
template <typename T>
Stack<T>& Stack<T>::operator=(const Stack<T> &stack_obj)
{
if ( &stack_obj != this )
{
Stack<T> temp(stack_obj);
std::swap(top, temp.top);
std::swap(size, temp.size);
}
return *this;
}
Believe it or not, this works. The code that used to be here was moved to the copy constructor, and thus the assignment operator is going to take advantage of the copy constructor.
To explain briefly, the code creates a temporary Stack<T> object from the passed-in stack_obj (this is why you need a working copy constructor). Then all that is done is to swap out the this members with the temporary members. Last, the temp dies off with the old data (this is why the destructor needs to be working correctly).
Note that you need to swap all of the members, so if you add more members to the Stack<T> class, you need to swap them in the assignment operator.
Once you have the basic 3 functions (copy, assign, destroy) written correctly, then and only then should you write functions that return Stack<T> by value, or write functions that take a Stack<T> by value.
You may have other issues with operator + that lie outside the scope of what this answer is presenting to you, but the gist of it is that your class requires that it has correct copy semantics before implementing +.

C++ Linked List Assignment Operator Problem

I am writing an Ordered Linked List class definition (OLList). I have written the assignment operator function, but when I try to test it by chaining assignment operations, the program gets caught in the while loop of the OLList::copy function. I know this because I tested using console prints.
//OLList.h
struct Node {
ListItem item;
Node *next;
};
class OLList {
public:
OLList& OLList::operator =(const OLList& rhs)
{
if (this != &rhs) {
destroy();
copy(rhs);
}
return *this;
}
void OLList::destroy()
{
Node *current_node = this->headM;
Node *next_node;
while(current_node->next != nullptr)
{
next_node = current_node->next;
delete(current_node);
current_node = next_node;
}
return;
}
void OLList::copy(const OLList& source)
{
Node *new_node, *current_node;
Node *current_source_node = source.headM;
this->headM->item = source.headM->item;
current_node = this->headM;
while(current_source_node->next != nullptr)
{
new_node = new(Node);
current_node->next = new_node;
current_node = current_node->next;
current_source_node = current_source_node->next;
current_node->item = current_source_node->item;
}
return;
}
}
Below is the code used to test the class. I have made sure that the print() function works fine so that's definitely not an issue.
//main.cpp
int main()
{
OLList the_list;
the_list.insert(1);
the_list.insert(2);
OLList second_list;
second_list.insert(3);
second_list.insert(4);
OLList third_list;
third_list.insert(5);
third_list.insert(6);
third_list = second_list = the_list;
third_list.print();
}
When it is compiled and run, the program never terminates as it is caught in the loop mentioned above.
Your destroy() method will fail if headM is nullptr. You should be using while(current_node != nullptr) instead of while(current_node->next != nullptr). But more importantly, it doesn't reset headM to nullptr after destroying the list. So after operator= calls destroy(), headM is no longer in a valid state for copy() to use.
Your copy() method is similarly not checking if either source or target headM are nullptr. But more importantly, it assumes the target list is empty beforehand, otherwise it leaks memory, if it does not crash outright (per above). And frankly, it simply is not coded correctly in general to copy one list to another.
So, your code is invoking undefined behavior, this anything could happen.
Like #PaulMcKenzie stated in comments, you really should be using a proper copy constructor instead (and a destructor - and since you are clearly using C++11 or later, a move constructor and move assignment operator, too - see the Rule of 5). Your assignment operator can then be implemented using your copy constructor (and likewise for move assignment).
Try something more like this:
struct Node {
ListItem item;
Node *next = nullptr;
Node(const ListItem &value) : item(value) {}
};
class OLList {
private:
Node *headM = nullptr;
public:
OLList() = default;
OLList(const OLList &src)
{
Node *current_source_node = src.headM;
Node **current_node = &headM;
while (current_source_node)
{
*current_node = new Node(current_source_node->item);
current_node = &((*current_node)->next);
current_source_node = current_source_node->next;
}
/* alternatively:
Node *current_source_node = src.headM;
while (current_source_node) {
insert(current_source_node->item);
}
*/
}
OLList(OLList&& src)
{
src.swap(*this);
}
~OLList()
{
Node *next_node;
while (headM)
{
next_node = headM->next;
delete headM;
headM = next_node;
}
}
void clear() {
OLList().swap(*this);
}
OLList& operator=(const OLList& rhs)
{
if (this != &rhs) {
OLList(rhs).swap(*this);
}
return *this;
}
OLList& OLList::operator=(OLList&& rhs)
{
OLList(std::move(rhs)).swap(*this);
return *this;
}
void swap(OLList &other) {
std::swap(headM, other.headM);
}
void insert(const ListItem &value) {
...
}
void print() const {
...
}
...
};

Error on build but no compile errors

I am having some trouble with my homework and could use your help.
I am getting some sort of error when I try to run my program. When i compile it i get the success mssage but when i try to run it i get a popup with the error "Unhandled exception at 0x011b18d2 in Project 2.exe: 0xC0000005: Access violation reading location 0xccccccd0." If anyone can help me i would appreciate it, thank you.
This is the code i was assigned to build on (this cannot be changed)
#include <iostream >
#include "stack.h"
using namespace std ;
int main ()
{
Stack < int > s1 , s2 ;
int element ;
s1 . push (1); s1 . push (2); s1 . push (3);
s1 . pop ( element );
cout << " s1 popped element : " << element << endl ;
s2 = s1 ;
s2 . push (4);
s2 . pop ( element );
cout << " s2 popped element : " << element << endl ;
s1 . pop ( element );
cout << " s1 popped element : " << element << endl ;
s2 . makeEmpty ();
s2 . isEmpty () ? cout << " s2 is empty \n": cout << " s2 is not empty \n ";
system ("pause");
return 0;
}
This is what i wrote to compliment the code above
template <class DataType>
struct Node{
DataType info;
Node<DataType>*next;
};
template <class DataType>
class Stack
{
public:
Stack();
void push(DataType elementToPush);
bool pop(DataType & poppedElement);
bool peek(DataType & topElement);
Stack(const Stack<DataType> &element); // Copy constructor
~Stack(); // Destructor
Stack<DataType> & operator=(const Stack<DataType> &element); //Overload assignment operator
bool isEmpty()const;
void makeEmpty();
private:
Node<DataType>*top;
Node<DataType>*header;
inline void deepCopy(const Stack<DataType> & original);
};
template<class DataType>
Stack<DataType>::Stack()
{
Node<DataType>*top=new Node<DataType>;
}
template<class DataType> // Remove the node at the front of the list and return the element
bool Stack<DataType>::pop(DataType & poppedElement)
{
Node<DataType>*ptr=top;
ptr=ptr->next;
Node<DataType>*ptr2=ptr->next;
top->next=ptr2;
poppedElement = ptr->info;
delete ptr;
return true;
}
template<class DataType> // Return the element at the front of the list wothout deleting it
bool Stack<DataType>::peek(DataType & topElement)
{
if(top->next==NULL)
return false;
topElement=top->next->info;
return true;
}
template<class DataType> // Make a new node for the element and push it to the front of the list
void Stack<DataType>::push(DataType elementToPush)
{
Node<DataType>*ptr=top;
Node<DataType>*ptr2=new Node<DataType>;
ptr2->info=elementToPush;
ptr2->next=ptr->next;
ptr->next=ptr2;
}
template<class DataType> // Check to see if the list is empty
bool Stack<DataType>::isEmpty()const
{
return top->next==NULL;
}
template<class DataType> // Empry the list out
void Stack<DataType>::makeEmpty()
{
Node<DataType>*ptr=top;
while(top->next != NULL)
{
while(ptr->next != NULL)
ptr->next;
delete ptr->next;
}
}
template<class DataType> // Deep copy
inline void Stack<DataType>::deepCopy(const Stack<DataType> & original)
{
Node<DataType>*copyptr=new Node<DataType>;
Node<DataType>*originalptr=top;
while(originalptr != NULL)
{
originalptr=originalptr->next;
copyptr->next=new Node<DataType>;
copyptr->info=originalptr->info;
}
}
template<class DataType> // Copy Constructor
Stack<DataType>::Stack(const Stack<DataType> &element)
{
deepCopy(element);
}
template<class DataType> // Destructor
Stack<DataType>::~Stack()
{
makeEmpty();
}
template<class DataType> // Overload assignment operator
Stack<DataType> & Stack<DataType>::operator=(const Stack<DataType> &element)
{
if(this == &element)
return *this;
makeEmpty();
deepCopy(element);
return *this;
}
I got pushback on my previous answer. Maybe this one will be better received. If you don't like my choice of white space, that is what pretty-printers are for. The code below is the original code reformatted. My thoughts are included as interlinear gloss.
Node is an implementation detail of your Stack. It should be scoped as a private type declaration, putting here pollutes the namespace. Also, if this class had a constructor that either initialized next to nullptr or required it to be set explicitly, some bugs, such as the one you found, would be easier to diagnose. As it stands, after Node is constructed, next can point to a random memory location.
template <class DataType>
struct Node {
DataType info;
Consider using a smart pointer here.
Node<DataType>* next; };
template <class DataType>
class Stack {
public:
Stack();
The argument should be const& to avoid extra copying.
void push(DataType elementToPush);
bool pop(DataType& poppedElement);
This can be a const method.
bool peek(DataType& topElement);
element is a poor name. The copy constructor copies an entire stack, not just an element.
Stack(const Stack<DataType>& element); // Copy constructor
~Stack(); // Destructor
Stack<DataType>& operator=(const Stack<DataType>&
element); //Overload assignment operator
bool isEmpty() const;
void makeEmpty();
private:
Consider using a smart pointer here.
Node<DataType>* top;
header is not used. It should be deleted.
Node<DataType>* header;
inline void deepCopy(const Stack<DataType>& original); };
template<class DataType>
Stack<DataType>::Stack() {
top should be initialized to nullptr in a member initialization list. The empty node you
are using here is not required, it makes you code more complex, and you end up leaking it later.
Also, this is a major bug. You are assigning to a local here, not the member variable!
Node<DataType>* top = new Node<DataType>; }
template<class DataType> // Remove the node at the front of the list and return the element
bool Stack<DataType>::pop(DataType& poppedElement) {
If you want ptr to be top->next just say that.
Node<DataType>* ptr = top;
ptr = ptr->next;
This ptr2 variable is not needed. You just need top->next = top->next->next. Also note that the empty head element is adding noise here.
Node<DataType>* ptr2 = ptr->next;
top->next = ptr2;
poppedElement = ptr->info;
delete ptr;
You need to have tested for underflow to return false in that case.
return true; }
People are pretty forgiving about comments, but it is best if they are properly spelled and punctuated.
template<class DataType> // Return the element at the front of the list wothout deleting it
bool Stack<DataType>::peek(DataType& topElement) {
if (top->next == NULL) {
return false; }
topElement = top->next->info;
return true; }
template<class DataType> // Make a new node for the element and push it to the front of the list
void Stack<DataType>::push(DataType elementToPush) {
This variable is meaningless, just use top.
Node<DataType>* ptr = top;
ptr2 can be constructed with the values you need instead of being mutated afterwards. Try auto ptr2 = new Node<DataType> { elementToPush, ptr->next };. Also, consider using a smart pointer.
Node<DataType>* ptr2 = new Node<DataType>;
ptr2->info = elementToPush;
ptr2->next = ptr->next;
ptr->next = ptr2; }
template<class DataType> // Check to see if the list is empty
bool Stack<DataType>::isEmpty()const {
return top->next == NULL; }
This function is just broken. You need to rethink it.
template<class DataType> // Empry the list out
void Stack<DataType>::makeEmpty() {
Node<DataType>* ptr = top;
while (top->next != NULL) {
One while loop will do you. Lists are linear, not square.
while (ptr->next != NULL) {
This statement has no effect; it does nothing. Your compiler should be warning about that, turn on warnings, or turn the warning level up.
ptr->next; }
delete ptr->next; } }
This is very broken too. You need to iterate over two lists, so you need two iterator variables. One iterator is the stuff you are copying and just needs to be bumped along as you read it. The other is mutating the current object and has slightly more book keeping.
template<class DataType> // Deep copy
inline void Stack<DataType>::deepCopy(const Stack<DataType>& original) {
Node<DataType>* copyptr = new Node<DataType>;
Node<DataType>* originalptr = top;
while (originalptr != NULL) {
originalptr = originalptr->next;
copyptr->next = new Node<DataType>;
copyptr->info = originalptr->info; } }
template<class DataType> // Copy Constructor
Stack<DataType>::Stack(const Stack<DataType>& element) {
deepCopy(element); }
template<class DataType> // Destructor
Stack<DataType>::~Stack() {
Note that makeEmpty does not delete your empty head node. This will leak a node.
makeEmpty(); }
template<class DataType> // Overload assignment operator
Stack<DataType>& Stack<DataType>::operator=(const Stack<DataType>&
element) {
if (this == &element) {
return *this; }
makeEmpty();
Again, your empty head node is causing pain here. Does deepCopy create the empty head node or not? Your use of it in your copy constructor seems to assume it does. Your use of it here seems to assume it does not. In fact, I think the problem is that makeEmpty does not delete your head node, if it did, both this function and your destructor would work properly.
deepCopy(element);
return *this; }
What you are seeing is a run-time error, not a build error. And your IDE reports a successful build, not your debugger. Your debugger is what allows you to trace through the program line-by-line and inspect the values of your variables.
Compare your code to the following.
template <class DataType>
struct Node {
DataType info;
Node<DataType>* next; };
template <class DataType>
class Stack {
public:
Stack();
void push(DataType elementToPush);
bool pop(DataType& poppedElement);
bool peek(DataType& topElement);
Stack(const Stack<DataType>& element);
~Stack();
Stack<DataType>& operator=(const Stack<DataType>& element);
bool isEmpty()const;
void makeEmpty();
private:
Node<DataType>* top;
inline void deepCopy(const Stack<DataType>& original); };
// Linked list stack implementation.
template<class DataType>
Stack<DataType>::Stack() {
// Head of the list. Not actually used for anything. Why is this here?
top = new Node<DataType>; }
// Remove the node at the front of the list and return the element
// Does not check for underflow.
template<class DataType>
bool Stack<DataType>::pop(DataType& poppedElement) {
Node<DataType>* ptr = top->next;
Node<DataType>* ptr2 = ptr->next;
top->next = ptr2;
poppedElement = ptr->info;
delete ptr;
return true; }
// Return the element at the front of the list without deleting it
template<class DataType>
bool Stack<DataType>::peek(DataType& topElement) {
if (top->next == NULL) {
return false; }
topElement = top->next->info;
return true; }
// Make a new node for the element and push it to the front of the list
template<class DataType>
void Stack<DataType>::push(DataType elementToPush) {
Node<DataType>* ptr2 = new Node<DataType>;
ptr2->info = elementToPush;
ptr2->next = top->next;
top->next = ptr2; }
// Check to see if the list is empty
template<class DataType>
bool Stack<DataType>::isEmpty()const {
return top->next == NULL; }
// Empty the list out
template<class DataType>
void Stack<DataType>::makeEmpty() {
while (top->next != NULL) {
Node<DataType>* ptr = top->next;
top->next = ptr->next;
delete ptr; } }
// Deep copy
template<class DataType>
inline void Stack<DataType>::deepCopy(const Stack<DataType>& original) {
Node<DataType>* origiter = original.top;
Node<DataType>* thisiter = top;
while (origiter->next != NULL) {
thisiter->next = new Node<DataType>(*(origiter->next));
origiter = origiter->next;
thisiter = thisiter->next; }
thisiter->next = NULL; }
// Copy Constructor
template<class DataType>
Stack<DataType>::Stack(const Stack<DataType>& element) {
deepCopy(element); }
// Destructor
template<class DataType>
Stack<DataType>::~Stack() {
// This leaks because the head node is still there.
makeEmpty(); }
// Overload assignment operator
template<class DataType>
Stack<DataType>& Stack<DataType>::operator=(const Stack<DataType>&
element) {
if (this == &element) {
return *this; }
makeEmpty();
deepCopy(element);
return *this; }