I am currently writing a program to mimic the functions of a linked list. I think the the code is crashing due to memory leaks, but I cannot figure out where they might be. I have tried deleting after every new, but when I delete it causes the functions to not operate correctly. I also think I my push_back() function may not be operating correctly, because my output does not match the output of std::list. Any help would be greatly appreciated. Thanks!
When I run the program this is the output I receive: 4000 33 100 3 33 100 423 423 100 33 200 100 4000 double free or corruption (fasttop) Aborted (core dumped)
This is the output I am expecting: 4000 33 100 3 33 100 423 423 100 423 33 200 100
Header File:
#ifndef MYLIST_H
#define MYLIST_H
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::cerr;
using std::string;
template <typename T>
class Node
{
    public:
        T m_element;
        Node<T> *m_prev;
        Node<T> *m_next;
        // Helps you make a dummy/sentinel/junk node
        Node(Node<T> *in_prev, Node<T> *in_next): 
            m_prev(in_prev), m_next(in_next){}
        Node(const T &x, Node<T> *in_prev, Node<T> *in_next): 
            m_element(x), m_prev(in_prev), m_next(in_next){}
};
template <typename T>
class MyList
{
    private:
        Node<T> *m_sentinel = nullptr;
        int m_size;
    public:
        // A list size of 0 will have 1 sentinel node.
        MyList();
        ~MyList();
        MyList<T> & operator=(const MyList<T> &source);
        MyList(const MyList<T> &source);
        T & front();
        T & back();
        void assign(int count, const T &value);
        // Default list size of 0, with one sentinel node, as above in constructor
        void clear();
        void push_front(const T &x);
        void push_back(const T &x);
        void pop_back();
        void pop_front();
        // Simplified version that only takes one int position.
        // Inserts before element at position i.
        // Not exactly like std::
        // You do NOT need a special case for 0-big lists (i.e., no if size == 0)
        // You should be able to insert in 0 big list.
        void insert(int i, const T &x);
        // Removes all elements in list that are equal to value. 
        // You do NOT need a special case for 0-big lists (i.e., no if size == 0).
        void remove(T value);
        // Removes element at position i.
        void erase(int i);
        void reverse();
        bool empty();
        int size();
        // Mimicking C++ iterator trickery from here down. 
        // You don't need to edit these two.
        int begin()
        {
            return 0;
        }
        int end()
        {
            return size();
        }
};
#include "MyList.hpp"
#endif
HPP File:
template <typename T>
MyList<T>::MyList()
{
  m_sentinel = new Node<T>(nullptr, nullptr);
  m_sentinel->m_next = m_sentinel;
  m_sentinel->m_prev = m_sentinel;
  m_size = 0;
}
template <typename T>
MyList<T>::~MyList()
{
  Node<T> *temp;
  for(int i = 0; i < m_size; i++)
  {
    temp = m_sentinel->m_prev;
    delete m_sentinel->m_prev;
    m_sentinel->m_prev = temp->m_prev;
  }
  delete m_sentinel;
  m_sentinel = nullptr;
}
template <typename T>
MyList<T> & MyList<T>::operator=(const MyList<T> &source)
{
  this->~MyList();
  Node<T>* tempNode = new Node<T>(nullptr, nullptr);
  tempNode = source.m_sentinel->m_next;
  m_sentinel = new Node<T>(nullptr, nullptr);
  m_sentinel->m_next = m_sentinel;
  m_sentinel->m_prev = m_sentinel;
  for(int i = 0; i < source.m_size; i++)
  {
    this->push_back(tempNode->m_element);
    tempNode = tempNode->m_next;
  }
}
template <typename T>
MyList<T>::MyList(const MyList<T> &source)
{
  *this = source;
}
template <typename T>
T & MyList<T>::front()
{
  return m_sentinel->m_next->m_element;
}
template <typename T>
T & MyList<T>::back()
{
  return m_sentinel->m_prev->m_element;
}
template <typename T>
void MyList<T>::assign(int count, const T &value)
{
  while(m_size)
  {
    this->pop_back();
  }
  for(int i = 0; i < count; i++)
  {
    this->push_back(value);
  }
  m_size = count;
}
// Default list size of 0, with one sentinel node, as above in constructor
template <typename T>
void MyList<T>::clear()
{
  while(m_size)
  {
    this->pop_back();
  }
}
template <typename T>
void MyList<T>::push_front(const T &x)
{
  Node<T> *tempNode = new Node<T>(x, m_sentinel, m_sentinel->m_next);
  m_sentinel->m_next->m_prev = tempNode;
  m_sentinel->m_next = tempNode;
  m_size++;
}
template <typename T>
void MyList<T>::push_back(const T &x)
{
  Node<T> *tempNode = new Node<T>(x, m_sentinel->m_prev, m_sentinel);
  m_sentinel->m_prev = tempNode;
  tempNode->m_prev->m_next = tempNode;
  m_size++;
}
template <typename T>
void MyList<T>::pop_back()
{
  //edit
  if(m_size != 0)
  {
    //check if temp node is needed
    //Node<T> *tempNode = m_sentinel->m_prev;
    m_sentinel->m_prev = m_sentinel->m_prev->m_prev;
    delete m_sentinel->m_prev->m_next;
    m_sentinel->m_prev->m_next = nullptr;
    m_sentinel->m_prev->m_next = m_sentinel;
    //delete tempNode;
    m_size--;
  }
}
template <typename T>
void MyList<T>::pop_front()
{
  if(m_size != 0)
  {
    m_size--;
    m_sentinel->m_next = m_sentinel->m_next->m_next;
    delete m_sentinel->m_next->m_prev;
    m_sentinel->m_next->m_prev = nullptr;
    m_sentinel->m_next->m_prev = m_sentinel;
  }
}
// Simplified version that only takes one int position.
// Inserts before element at position i.
// Not exactly like std::
// You do NOT need a special case for 0-big lists (i.e., no if size == 0)
// You should be able to insert in 0 big list.
template <typename T>
void MyList<T>::insert(int i, const T &x)
{
  if(i >= 0 && i < m_size)
  {
    Node<T> *tempNode = m_sentinel;
    for(int j = 0; j < i; j++)
    {
      tempNode = tempNode->m_next;
    }
    Node<T> *insertNode = new Node<T>(x, tempNode, tempNode->m_next);
    tempNode->m_prev->m_next = insertNode;
    tempNode->m_next->m_prev = insertNode;
    m_size++;
  }
}
// Removes all elements in list that are equal to value.
// You do NOT need a special case for 0-big lists (i.e., no if size == 0).
template <typename T>
void MyList<T>::remove(T value)
{
  Node<T> *currentNode = m_sentinel->m_next;
  Node<T> *tempNode = currentNode->m_next;
  int counter = m_size;
  for(int i = 0; i < counter; i++)
  {
    if(currentNode->m_element == value)
    {
      currentNode->m_prev->m_next = currentNode->m_next;
      currentNode->m_next->m_prev = currentNode->m_prev;
      tempNode = currentNode->m_next;
      m_size--;
      delete currentNode;
      currentNode = nullptr;
      currentNode = tempNode;
    }
    else
    {
      currentNode = currentNode->m_next;
    }
  }
}
// Removes element at position i.
template <typename T>
void MyList<T>::erase(int i)
{
  if(i >= 0 && i < m_size)
  {
    Node<T> *currentNode = m_sentinel->m_next;
    for(int j = 0; j < i; j++)
    {
      currentNode = currentNode->m_next;
    }
    currentNode->m_prev->m_next = currentNode->m_next;
    currentNode->m_next->m_prev = currentNode ->m_prev;
    delete currentNode;
    currentNode = nullptr;
    m_size--;
  }
}
template <typename T>
void MyList<T>::reverse()
{
  Node<T> *nextNode = m_sentinel->m_next;
  Node<T> *prevNode = m_sentinel->m_prev;
  for(int i = 0; i < (m_size/2); i++)
  {
    std::swap(nextNode->m_element, prevNode->m_element);
    nextNode = nextNode->m_next;
    prevNode = prevNode->m_prev;
  }
}
template <typename T>
bool MyList<T>::empty()
{
  if(m_size==0)
  {
    return true;
  }
  else
  {
    return false;
  }
}
template <typename T>
int MyList<T>::size()
{
  return m_size;
}
Driver to test if MyList function works like std::list:
#include <list>
#include "MyList.h"
int main()
{
    MyList<int> l;
    //std::list<int> l;
    l.push_back(4000);
    l.push_back(200);
    l.push_back(100);
    cout << l.front() << endl;
    l.front() = 33;
    cout << l.front() << endl;
    cout << l.back() << endl;
        cout << l.size() << endl;
    l.push_back(4000);
    l.push_back(200);
    l.push_back(100);
    cout << l.front() << endl;
    cout << l.back() << endl;
    l.push_front(423);
    cout << l.front() << endl;
    MyList<int> sink;
    sink = l;
    cout << sink.front() << endl;
    cout << sink.back() << endl;
    l.insert(l.begin(), 3);
    l.insert(l.end(), 20);
    l.pop_front();
    l.reverse();
    int j = 0;
    for(auto i = 0; i < l.size(); i++)
    {
        cout << l.back() << endl;
        l.pop_back();
        j++;
    }
    return 0;
}
Thanks!
UPDATED COPY CONSTRUCTOR AND ASSIGNMENT OPERATOR
template <typename T>
MyList<T> & MyList<T>::operator=(const MyList<T> &source)
{
  MyList<T> temp(source);
  std::swap(temp.m_sentinel, m_sentinel);
  return *this;
}
template <typename T>
MyList<T>::MyList(const MyList<T> &source)
{
  Node<T> *temp = source.m_sentinel;
  m_sentinel = nullptr;
  while(temp != nullptr)
  {
    push_back(temp->m_element);
  }
}
Now it gives me a segfault and crashes when calling the push_back() function. I'm lost. I've tried implementing this several different ways but every time I either get a double free or a segfault.