# CSE332S Object-Oriented Programming in C++ (Lecture 13)

## Memory layout of a C++ program, variables and their lifetimes

### C++ Memory Overview

4 major memory segments

- Global: variables outside stack, heap
- Code (a.k.a. text): the compiled program
- Heap: dynamically allocated variables
- Stack: parameters, automatic and temporary variables (all the variables that are declared inside a function, managed by the compiler, so must be fixed size)
- _For the dynamically allocated variables, they will be allocated in the heap segment, but the pointer (fixed size) to them will be stored in the stack segment._

Key differences from Java

- Destructors of automatic variables called when stack frame where declared pops
- No garbage collection: program must explicitly free dynamic memory

Heap and stack use varies dynamically

Code and global use is fixed

Code segment is "read-only"

```cpp
int g_default_value = 1;

int main (int argc, char **argv) {
  Foo *f = new Foo;  

  f->setValue(g_default_value);

  delete f; // programmer must explicitly free dynamic memory

  return 0;
}

void Foo::setValue(int v) {
  this->m_value = v;
}
```

![Image of memory layout](https://notenextra.trance-0.com/CSE332S/CPP_Function_Memory.png)

### Memory, Lifetimes, and Scopes

Temporary variables

- Are scoped to an expression, e.g., `a = b + 3 * c;`

Automatic (stack) variables

- Are scoped to the duration of the function in which they are declared

Dynamically allocated variables

- Are scoped from explicit creation (new) to explicit destruction (delete)

Global variables

- Are scoped to the entire lifetime of the program
- Includes static class and namespace members
- May still have initialization ordering issues

Member variables

- Are scoped to the lifetime of the object within which they reside
- Depends on whether object is temporary, automatic, dynamic, or global

**Lifetime of a pointer/reference can differ from the lifetime of the location to which it points/refers**

## Direct Dynamic Memory Allocation and Deallocation

```cpp
#include <iostream>
using namespace std;
int main (int, char *[]) { 
  int * i = new int;      // any of these can throw bad_alloc
  int * j = new int(3);  
  int * k = new int[*j];                          
  int * l = new int[*j];                                                
  for (int m = 0; m < *j; ++m) {   // fill the array with loop                        
    l[m] = m;                                                        
  }
  delete i; // call int destructor                                   
  delete j; // single destructor call                        
  delete [] k;  // call int destructor for each element                          
  delete [] l;    
  return 0;
}
```

## Issues with direct memory management

### A Basic Issue: Multiple Aliasing

```cpp
int main (int argc, char **argv) {
  Foo f;
  Foo *p = &f;
  Foo &r = f;
  delete p;
  return 0;
}
```

Multiple aliases for same object

- `f` is a simple alias, the object itself
- `p` is a variable holding a pointer
- `r` is a variable holding a reference

What happens when we call delete on p?

- Destroy a stack variable (may get a bus error there if we’re lucky)
- If not, we may crash in destructor of f at function exit
- Or worse, a local stack corruption that may lead to problems later

Problem: object destroyed but another alias to it was then used (**dangling pointer issue**)

### Memory Lifetime Errors

```cpp
Foo *bad() {
  Foo f;
  return &f; // return address of local variable, f is destroyed after function returns
}

Foo &alsoBad() {
  Foo f;
  return f; // return reference to local variable, f is destroyed after function returns
}

Foo mediocre() {
  Foo f;
  return f; // return copy of local variable, f is destroyed after function returns, danger when f is a large object
}

Foo * good() {
  Foo *f = new Foo;
  return f; // return pointer to local variable, with new we can return a pointer to a dynamically allocated object, but we must remember to delete it later
}

int main() {
  Foo *f = &mediocre(); // f is a pointer to a temporary object, which is destroyed after function returns, f is invalid after function returns
  cout << good()->value() << endl; // good() returns a pointer to a dynamically allocated object, but we did not store the pointer, so it will be lost after function returns, making it impossible to delete it later.
  return 0;
}
```

Automatic variables

- Are destroyed on function return
- But in bad, we return a pointer to a variable that no longer exists
- Reference from also_bad similar
- Like an un-initialized pointer

What if we returned a copy?

- Ok, we avoid the bad pointer, and end up with an actual object
- But we do twice the work (why?)
- And, it’s a temporary variable (more on this next)

We really want dynamic allocation here

Dynamically allocated variables

- Are not garbage collected
- But are lost if no one refers to them: called a "**memory leak**"

Temporary variables

- Are destroyed at end of statement
- Similar to problems w/ automatics

Can you spot 2 problems?

- One with a temporary variable
- One with dynamic allocation

### Double Deletion Errors

```cpp
int main (int argc, char **argv) {
  Foo *f = new Foo;  
  delete f;
  // ... do other stuff
  delete f; // will throw an error because f is already deleted
  return 0;
}   
```

What could be at this location?

- Another heap variable
- Could corrupt heap

## Shared pointers and the RAII idiom

### A safer approach using smart pointers

C++11 provides two key dynamic allocation features

- `shared_ptr` : a reference counted  pointer template to alias and manage objects allocated in dynamic memory (we’ll mostly use the shared_ptr smart pointer in this course)
- `make_shared` : a function template that dynamically allocates and value initializes an object and then returns a shared pointer to it (hiding the object’s address, for safety)

C++11 provides 2 other smart pointers as well

- `unique_ptr` : a more complex but potentially very efficient way to transfer ownership of dynamic memory safely (implements C++11 “move semantics”)
- `weak_ptr` : gives access to a resource that is guarded by a shared_ptr without increasing reference count (can be used to prevent memory leaks due to circular references)

### Resource Acquisition Is Initialization (RAII)

Also referred to as the "Guard Idiom"

- However, the term "RAII" is more widely used for C++

Relies on the fact that in C++ a stack object’s destructor is called when stack frame pops

Idea: we can use a stack object (usually a smart pointer) to hold the ownership of a heap object, or any other resource that requires explicit clean up

- Immediately initialize stack object with the allocated resource
- De-allocate resource in the stack object’s destructor

### Example: Resource Acquisition Is Initialization (RAII)

```cpp
shared_ptr<Foo> createAndInit() {
 shared_ptr<Foo> p = 
    make_shared<Foo> ();
 init(p);// may throw exception
 return p;
}

int run () {
  try {
    shared_ptr<Foo> spf =
                 createAndInit();
    cout << “*spf is ” << *spf;
  } catch (...) {
     return -1
  } 
  return 0;
}
```

RAII idiom example using shared_ptr

```cpp
#include <memory>
using namespace std;
```

- `shared_ptr<X>` assumes and maintains ownership of aliased X
- Can access the aliased X through it (*spf)
- `shared_ptr<X>` destructor calls delete on address of owned X when it’s safe to do so (per reference counting idiom discussed next)
- Combines well with other memory idioms

### Reference Counting

Basic Problem

- Resource sharing is often more efficient than copying
- But it’s hard to tell when all are done using a resource
- Must avoid early deletion
- Must avoid leaks (non-deletion)

Solution Approach

- Share both the resource and a counter for references to it
- Each new reference increments the counter
- When a reference is done, it decrements the counter
- If count drops to zero, also deletes resource and counter
- "last one out shuts off the lights"

### Reference Counting Example

```cpp
shared_ptr<Foo> createAndInit() {
 shared_ptr<Foo> p = 
    make_shared<Foo> ();
 init(p);// may throw exception
 return p;
}

int run () {
  try {
    shared_ptr<Foo> spf =
                 createAndInit();
    shared_ptr<Foo> spf2 = spf;
    // object destroyed after
    // both spf and spf2 go away
  } catch (...) {
     return -1
  } 
  return 0;
}
```

Again starts with RAII idiom via shared_ptr

- `spf` initially has sole ownership of aliased X
- `spf.unique()` would return true
- `spf.use_count` would return 1

`shared_ptr<X>` copy constructor increases count, and its destructor decreases count

`shared_ptr<X>` destructor calls delete on the pointer to the owned X when count drops to 0
