Lecture 12 - Oct 3, 2023

Summary

In this lecture, we discuss pointers in objects, destructors and when we need them.

Last lecture

Memory organization: stack and heap, pointers, and dynamic memory allocation.

Today

Destructors and pointers in objects.

Dynamically allocated memory

The problem when if we dynamically allocate memory in a class is to have a memory leak.

Example
class Student {
    private:
        int *grades; string name;
    public:
        Student();
        Student(int);
};

Student::Student() {
    grades = nullptr;
}

Student::Student(int numLabs) {
    grades = new int[numLabs];
}

int main() {
    // dynamically allocates 3 integers
    Student x(3);
    return 0;

    // we did not deallocate them
    // this will create a memory leak!
}

diagrams/lecture11-diagram2.svg

The solution is in defining destructors.

Example
class Student {
    :
        int *grades; string name;
    public:
        Student();
        Student(iprivatent);
        // the destructor must be public
        ~Student(); // no return, like constructors
                    // no parameters
};

...

Student::~Student() {
    if (grades != nullptr) {
        delete[] grades;
    }
}

The default destructor exists by default, and is called when the object goes out of scope.

If you dynamically allocate memory in your class, you will need a destructor to free up this memory space.

Example
// main.cpp

int main() {
    Student x(3);
    return 0;
}

The destructor of x will be called by default at the end of main. If grades is !nullptr, we will free dynamically allocated space.

Pointers to objects

Can pointers point to objects? Yes!

Example

Use case to dynamically allocate memory for an object on the heap.

class ComplexNum {
    double real;
    double img;
    ComplexNum(double r, double i) { real = r; img = i; }
};

int main() {
    ComplexNum, x(3,4);
    x.real = 2;
    ComplexNum *p;
    p = &x;

    // ->: access member at address stored in p
    p->real = 7;

    // or  de-reference p then use .
    // (*p).real = 7;
}

Pointers to objects in objects

Pacha’s note

I added return 0; to the main function, otherwise it would not compile.

class ComplexNum {
 public:
  double real;
  double img;
  ComplexNum* next;

  ComplexNum() {
    real = 0;
    img = 0;
    next = nullptr;
  }

  ~ComplexNum() {
    if (next != nullptr) delete next;
  }
};

int main() {
    ComplexNum* px = new ComplexNum; // 1
  px->next = new ComplexNum; // 2
  px->next->real = 8; // 3
  delete px; // calls the destructor
             // frees up memory that p is pointing to
  return 0;
}

diagrams/lecture12-diagram1.svg

What about next in p?

  • delete px calls destructor on A.
  • Then delete next of A, which is B.
  • Then calls destructor on B , next is null.