## Chapter 18 - Memory Management

## Chapter Goals

- To learn the different categories of memory
- To understand how to avoid common memory management errors
- To learn to write and use constructors and destructors
- To be able to create classes that manage their own memory allocation and deallocation

## 18.1 Categories of Memory

Memory is divided into four categories:

1. Code - instructions for all global and member functions
2. Static Data - global variables, and all local and member variables declared `static`
3. Run-time Stack - automatics (local non-static)
4. Free Store (Heap) - dynamically allocated (`new`)

## 18.1.1 Code Memory

- Contains all machine instructions for functions and methods
- May be referenced through pointers
  - (See next chptr. and Lippman[1] for discussion on function pointers)

## 18.1.2 Static Data Memory

- Globals, static locals and static member variables
- Size of statics can be known prior to execution
- Created only once
- Initialized only once, before `main`

## 18.1.3 The Run-time Stack

- Used for functions
- Every call is a new activation record on stack
- Activation record contains:
  - Parameters
  - Return pointer
  - Local variables
  - Other machine-specific stuff
- Return from function decrements stack pointer
- Drawbacks:
  - Local variables cease to exist when function exits
  - Size of locals must be known at compile time

## 18.1.4 The Run-time Stack (example)

If function f has called g, which, in turn, called h, then the stack looks something like this: |

## Heap Memory

The heap, or free store:

- Request storage area with `new`
- Values accessed through a pointer
- Neither the number of values, nor the size of a particular value need be known at compile time
- Lifetime of a value is not tied to a function
- Values may persist after program ends
- Space returned to heap using `delete`

## 18.2 Common Memory Errors

- The programmer is responsible for memory management in C++
- Pointers can refer to memory in any of the four areas (above)
- Possible errors include:
  - Using a value that has not been initialized
  - Using a pointer to reference a memory location that is no longer valid (dangling pointer)
  - Forgetting to delete a dynamically allocated section of memory (memory leak)
  - Deleting a memory value that was never allocated
  - Deleting a dynamically allocated section of memory more than once

## 18.3 Constructors

(introduced in Chptr. 6)

- Same name as the class
- No return type
- Used to initialize class' attributes
- May be overloaded
- Only one c'tor is invoked at creation
- Implicitly invoked:

- Each time a block is entered where an object is declared
- For static objects - before `main` starts
- When an object is created on the heap with `new`
- When an object is passed by value
- For member objects, when aggregate is created
- When child is instantiated

## 18.3 (cont.) Constructors - String Example

E.g., `String` class, with underlying array on heap.

>     class String
>     {
>     public:
>        String(); // Default constructor
>        String(const char p[]); // Simple constructor
>        String(const String& right); // Copy constructor
>        ~String(); // Destructor
>        String& operator=(const String& right); // Assignment operator
>        String& operator+=(const String& right);
>        int length() const;
>        char& operator[](int index);
>        char operator[](int index) const;
>     private:
>        char* buffer;
>        int len;
>     };

## 18.3.1 Constructors with Arguments

- All constructors perform necessary initialization
- Example: Our c'tor must allocate space from heap and initialize:

  >     String::String(const char p[])
  >     {
  >        // Determine number of characters in string (strlen(p))
  >        len = 0;
  >        while (p[len] != '\0')
  >           len++;
  >        // Allocate buffer array, remember to make space for NULL character
  >        buffer = new char[len + 1];
  >        // Copy new characters (strcpy( buffer, p ))
  >        for (int i = 0; i < len; i++)
  >           buffer[i] = p[i];
  >        buffer[len] ='\0';
  >     }

  > (Operators as described in chptr. 17)

## 18.3.2 Default Constructors

- The constructor that takes no arguments
- Invoked without parenthesis, (except for unnamed temporaries
- Invoked:
  - When a variable is declared (or created from the heap) with no arguments
  - The only initializer for elements in an array
  - When object is member data of another class, and no initializer is provided

## 18.3.2 Default Constructors (cont.)

- Example: a `String` is, by default, an empty string

  >     String::String()
  >     {
  >        len = 0;
  >        buffer = NULL; // No need to allocate array to hold zero characters
  >     }

## 18.3.3 Copy Constructors

- Sole parameter is a const reference to an object of the same class
- Automatically supplied by compiler - memberwise (shallow) copy by default
- Calls copy c'tor of all member data
- Used to copy (clone) a value

## 18.3.3 Copy Constructors (cont.)

- Example: each `String` has its own dynamically allocated buffer, so default behavior is insufficient. We extend this behavior:

  >     String::String(const String& right)
  >     {
  >        int n = right.length();
  >        buffer = new char[n + 1];
  >        for (int i = 0; i < n; i++)
  >           buffer[i] = right[i];
  >        buffer[n] = '\0';
  >     }

## 18.3.3 Copy Constructors (cont.)

Invoked:

- Explicitly

  > String first("Fred");
  > String second(first); // second is initialized from first using copy constructor

- During initialization using `=`

> String third = first; // Also uses copy constructor

- When an object is passed by value

- Note: Use `const` when passing a reference to avoid copy overhead

## 18.3.4 Field Initializer Lists

Member data may be assigned in a field initializer list.

- Syntax only valid for constructors
- Used to avoid modifying an attribute twice (default c'tor, then in the body of the outer c'tor):

## 18.3.4 Field Initializer Lists (cont.)

>     class Employee
>     {
>     public:
>        Employee(String employee_name, double initial_salary);
>        . . .
>     private:
>        String name;
>        double salary;
>     };

>     Employee::Employee(String employee_name, double initial_salary)
>        : name(employee_name), salary(initial_salary)
>     {
>     }

## 18.3.4 Field Initializer Lists (cont.)

- Necessary to invoke base-class c'tors; otherwise, the base-class' default c'tor will be called:

  >     class TeachingAssistant : public Employee
  >     public:
  >        TeachingAssistant(String student_name);
  >     };

  >     TeachingAssistant::TeachingAssistant(String student_name)
  >        // Teaching assistants all get same starting salary
  >        : Employee(student_name, 5000)
  >     {
  >     }

## 18.3.4 Field Initializer Lists (cont.)

- Initializer lists must be used for:
  - Data fields declared as non-static `const`
  - References
  - To invoke a base class' c'tor (other than default)

## 18.3.4 Field Initializer Lists (cont.)

- E.g., necessary when assigning a size to a vector member:

  >     class PartDescription
  >     {
  >     public:
  >        PartDescription(String part_name,
  >           int inventory_number);
  >     private:
  >        const String name;
  >        const int part_number:
  >        vector<PartDescription*> subcomponents;
  >     };
  >     PartDescription::PartDescription(String part_name,
  >           int inventory_number)
  >        : name(part_name), part_number(inventory_number),
  >           subcomponents(3) {}

## 18.3.5 Assignment Operators

- Tasks performed are similar to copy c'tor
- Must do cleanup first
- Check for auto-assignment

## 18.3.5 Assignment Operators (cont.)

- Example: `String` class:

  >     String& String::operator=(const String& right)
  >     {
  >        if (this != &right)
  >        {
  >           delete[] buffer; // Get rid of old buffer
  >           len = right.length();
  >           buffer = new char[len + 1];
  >           for (int i = 0; i < len; i++)
  >              buffer[i] = right[i];
  >           buffer[len] = '\0';
  >        }
  >        return *this;
  >     }

## 18.4 Destructors

- Perform necessary housekeeping tasks
- Named ~ClassName, no arguments, no return type
- Implicitly called on:
  - Local variables, at end of block
  - Parameters, at end of function
  - Temporary variables, at end of statement
  - Heap values, when `delete` is called
  - Static values, when `main` terminates
  - Member data, when object is deallocated
  - Base class object, when derived class object is deallocated
  - Local variables, when an exception is thrown and execution leaves the block

## 18.4 Destructors (cont.)

Syntax 17.2 : Overloading Operator Definition
    ClassName::~ClassName()
    {
    	 statements
    }
Example: |
    String::~String()
    {
       delete[] buffer;
    }
Purpose: | Perform any housekeeping tasks that should be performed before an object is deleted.

## 18.4 Virtual Destructors (cont.)

- Make destructors for base classes `virtual`
- E.g.: Consider the following declarations

  >     class Employee
  >     {
  >        virtual ~Employee();
  >     };

  >     Employee::~Employee()
  >     {  cout << "Goodbye Employee\n"; }

## 18.4 Virtual Destructors (cont.)

>     class TeachingAssistant : public Employee
>     {
>        ~TeachingAssistant();
>     };

>     TeachingAssistant::~TeachingAssistant()
>     {  cout << "Goodbye TeachingAssistant\n"; }

Now consider the following:

> Employee *a = new TeachingAssistant();
> delete a;

## 18.4 (cont.) "The Big 3" (revisit)

The "Big 3":

1. Copy constructor
2. Assignment operator
3. Destructor

- All have default behavior, as does the default c'tor
- Behavior sometimes needs to be extended
- Reason to define one => reason to define all 3 (or all 4)
- Build all 3 on lower-level helper methods

## 18.4.1 The Class `auto_ptr`

- "Smart pointers"
- Declared in `<memory>`
- Good when the lifetime of the dynamic value is tied to another object
- Dynamic memory `auto_ptr` references is automatically recovered
- Very useful with exceptions

See also: Advanced Topic 18.2 - Overloading the memory management operators.

## 18.5 Reference Counting

- Simple rule: objects that allocate memory should clean it up
- Not usually that simple. Consider sharing:
- Two options:
  1. Designate one as owner (not always possible)
  2. Use reference counting

## 18.5 Reference Counting

- Store reference count w/the shared resource
- New object has reference count of 1:
- When data object is shared, reference count is incremented:

## 18.5 Reference Counting (cont.)

- When a references is destroyed (or reassigned), ref count is decremented:
- When the reference count is zero, the shared resource is deleted

## 18.5 Example: Reference Counting

Consider our `String` class: change the semantics for the assignment of strings so that two strings would share a common internal data buffer:

- Structure holding reference count and the buffer is moved to a nested class, `StringReference`
- References are managed by `reassign`
  - Takes ptr to a `StringReference`
  - If not NULL, reference to argument value is incremented
  - Reference to current value is decremented
  - If current reference is 0, object is deleted
  - Current object is set to refer to argument value

## 18.5 Example: Reference Counting (`sharedString.cpp`)

## 18.5 Reference Counting

Summary:

- Create separate classes for; reference, shared data
- Reference count in shared data object
- Reference assignment: increment ref count
- Reference destroyed: decrement ref count
- Ref count reaches zero: destroy shared data

Note:

- Beware of self-assignment
- Avoid cycles

## 18.6 Case Study: Matrices

Extend the matrix from 17.11:

- Represent arbitrary m x n matrix

- Need to allocate from heap

- The "big three" done per Section 18.4
- Access methods replace constants for row/column information
- Smaller member functions have been inlined
- (Changes have been highlighted)

## 18.6 Case Study: Matrices (`matrix2.h`)

## 18.6 Case Study: Matrices (`matrix2.cpp`)

## 18.6 Case Study: Matrices, (`matrixtest2.cpp`)

## Chapter Summary

1. Memory is divided into four areas
2. Pointers can refer to memory in any of the four areas
3. Initialization errors can occur in any of the four area
4. Stack-based memory is tied to function entry and exit
5. Simple objects can be sliced. Use references or pointers
6. C'tors tie memory allocation with object initialization. Copy c'tors create copies of objects
7. D'tors return resources when an object is deallocated. The "Big 3"
8. Reference counts can be used to track shared resources