#+TITLE: Smart Pointers
#+AUTHOR: JirR02

* Task

  The objective of this problem is to implement a reference-count smart pointer, with functionality similar to that of a std::shared_ptr. Smart pointers implement the same functionality as regular pointers, but additionally they automatically take care of deallocating the object they point to when it is no longer needed. Reference-count smart pointers achieve this by allocating and maintaining a counter in memory together with the actual pointed-to object, which represents the number of smart pointers currently referencing the object. Any time a new smart pointer to an object is created or one is destroyed, this counter has to be incremented or decremented, respectively. When the last smart pointer to an object is destroyed, the counter will be decremented to

  and the smart pointer will know that the object is no longer referenced; it can then deallocate the object.

  The following example illustrates the process:

  #+BEGIN_src cpp
  Smart a, b, c; 
  a = Smart(new tracked()); 
  // The smart pointer 'a' now points to the new
  // tracked object with a reference count of 1.
  c = Smart(new tracked()); 
  // Another smart pointer 'c' now points to the
  // second new tracked object with count 1.
  b = a;                  
  // 'a' and 'b' now both point to the first object,
  // their shared counter is incremented to 2.
  c = Smart();            
  // A null smart pointer is assigned to 'c'; the
  // smart pointer previously stored in 'c' is destroyed
  // and its counter is decremented. Since the count
  // is now zero, the second tracked object is deallocated.
  return;
  // At the end of the function, the smart 
  // pointers 'a' and 'b' are both destroyed, their 
  // counter is decremented twice, and the first 
  // tracked object is deallocated.
  #+END_src


  *Locations*: The declarations of the smart pointer class (Smart) and member functions is provided in file =smart.h=. The implementation of member functions should be done in file =smart.cpp=. Smart pointers encapsulate pointers to objects of class tracked, which is declared in file =smart.h=.

  *Structure*: In class Smart, member variable =ptr= represents the pointer (potentially shared by several object of class Smart) to the underlying pointed-to object. Member variable =count= represents the (shared) location containing the number of objects of class Smart currently holding the pointed-to object. Alternatively, both pointers may be =nullptr=, which corresponds to the notion of a /null/ smart pointer. A null smart pointer does not manage any memory.

  Objects pointed by smart pointers belong to class tracked, which is a linked list node where the next pointer is represented using a smart pointer. In particular, tests will use this structure to build linked lists with shared nodes, and check at the end that everything was correctly deallocated. To that end, every object of class tracked is tracked behind the scenes.

  Steps:

  1. Implement the default constructor for class =Smart=. The default constructor should create a null smart pointer.

  1. Implement the constructor =Smart(tracked* t)=. If =t==nullptr=, this should return a null smart pointer, otherwise, this should create a smart pointer to =t= with a reference count of \(1\). You may assume that =t= points to memory newly allocated by =new= that is not already being managed by another smart pointer.

  1. Implement the copy constructor =Smart(const Smart& src)= that returns a new smart pointer to the memory pointed to by =src= and increments the shared reference counter.

  1. Implement the destructor =~Smart()=. For non-null pointers, it should decrement the reference counter and deallocate the pointed-to object if the resulting count is zero.

  1. Implement the assignment operator =Smart& operator=(const Smart& src)= that creates a copy of the provided smart pointer (incrementing its reference counter) and decrements the counter of the smart pointer of the left hand side of the assignment (and potentially deallocates the memory it points to).

  *Optional*: Figure out the situations in which smart pointers are not suitable for memory management, in the sense that they may lead to memory leaks. You may look at the tests which leak memory for inspiration.

  *Note*: In order to *debug*, you may execute code that you place in the function =your_own_tests= located in the =smart.cpp= file. The main function has a =switch= statement that chooses between a series of functions for different test scenarios. This =switch= defaults to =your_own_tests=. So if you enter =-1= for =test_id=, =your_own_tests= will be executed.

* Solutions

  #+BEGIN_src cpp
  #include "smart.h"
  #include "tracker.h"

  Smart::Smart() {
    count = nullptr;
    ptr = nullptr;
  }

  Smart::Smart(tracked *t) /* TODO */ {
    if (t == nullptr) {
      count = nullptr;
      ptr = nullptr;
    } else {
      ptr = t;
      int *counter = new int;
      *counter = 1;
      count = counter;
    }
  }

  Smart::Smart(const Smart &src) /* TODO */ {
    count = src.count;
    ptr = src.ptr;
    if (count != nullptr)
      (*count)++;
  }

  Smart::~Smart() {
    if (count != nullptr) {
      (*count)--;
      if (*count == 0) {
        delete count;
        delete ptr;
        count = nullptr;
        ptr = nullptr;
      }
    }
  }

  Smart &Smart::operator=(const Smart &src) {
    if (src == *this)
      return *this;

    if (count != nullptr) {
      (*count)--;
      if (*count == 0) {
        delete count;
        delete ptr;
      }
    }

    count = src.count;
    ptr = src.ptr;

    if (count != nullptr)
      (*count)++;

    return *this;
  }

  tracked &Smart::operator*() { return *ptr; }

  const tracked &Smart::operator*() const { return *ptr; }

  tracked *Smart::operator->() { return ptr; }

  const tracked *Smart::operator->() const { return ptr; }

  bool Smart::operator==(const Smart &cmp) const { return (ptr == cmp.ptr); }

  bool Smart::operator!=(const Smart &cmp) const { return !(*this == cmp); }

  int Smart::get_count() const {
    if (this->count == nullptr)
      return 0;
    else
      return *(this->count);
  }

  void your_own_tests() {
    // EXTRA TESTS ?
  }

  // Answer to optional question
  /*
  FILL IN WITH YOUR ANSWER
  */
  #+END_src

  --------------

  Made by JirR02 in Switzerland 🇨🇭
