Lecture 22

Ownership

  • Owning the resource = unique_ptr (OWNS-A)

  • Not owning the resource = raw ptr (HAS-A)

  • Shared ownership? This is very rare shared_ptr

Shared_ptr

Example of shared_ptr:

{ 
  auto p1 = std::make_shared<MyClass>();
  if (...) {
    auto p2 = p1;
  } // p2 popped, pointer not deleted
} // p1 popped, pointer deleted
  • Shared ptrs maintain a reference const

Reference const

  • Count of all shared_ptrs pointing at the same object

  • Memory is freed when the # of shared_ptrs pointing to it is about to reach 0

  • Shared pointers have their own destructors (don't need to write one) - so you don't need to worry about double freeing in a list when you have shared ownership

Downsides of shared_ptrs

Do not do this:

Exception Safety Ctd.

3 levels of exception safety for a function f:

1. Basic Guarantee

  • if an exception occurs: program will be left in some valid state

  • no leaks

  • no corrupted data structures

  • class invariants maintained

2. Strong Guarantee (you want this most often)

  • if an exception is raised while executing f: state of program will be as it was before it was called

3. No Throw Guarantee

  • f will never throw or propogate an exception

  • f will always accomplish its task

Example of Exception Safety:

Question: Is C::f's exception safe? Answer: Analyze what each statement throws:

  • if a.g() throws:

    • Nothing in the program has happened yet

    • Conclusion: This is OK

  • if b.h() throws:

    • Effects of g must be undone to offer the strong guarantee

      • Very hard or impossible if g has non-local side-effects

    • Conclusion: Probably not exception safe

So what if A::g and B::h do not have non-local side-effects?

Answer: We can use copy-and-swap as such:

Solution to this problem: Would be better if the "swap" part was nothrow because copy assignment pointer cannot throw. Use pImpl Idiom.

FINAL SOLUTION:

Exception Safety and the STL: vectors

How do vectors deal with exception safety?

  • Encapsulate a heap-allocated array

    • Follows RAII

      • When a stack-allocated vector goes out of scope, the internal heap-allocated array is freed

Example of Exception Safety with Vector that's good:

What's happening in the code above?

  • v goes out of scope

  • Array is freed

  • MyClass destructor runs on all objects in the vector

  • everything is all good :)

Example of Exception Safety with Vector of pointers that's not so good:

What's happening in the code above?

  • v goes out of scope

  • Array is freed

  • Pointers don't have destructors though, so any objects pointed at by the pointers are not deleted resulting in MEMORY LEAK

  • v doesn't know whether the pointers in the array own the objects they point at

So why can't you fix this problem by writing this?

If you need to do this, that means the pointer Own the objects they point at, and if you just use unique_ptrs, then the deleting would happen by itself.

Example to a vector of pointers that's good:

What's happening in the code above?

  • v goes out scope

  • Array is freed

  • unique_ptr destructor runs, so the objects are deleted

  • No need for any explicit deallocation

How does vector<T>::emplace_back deal with exception safety?

Copy Constructor:

  • Offers the strong guarantee

    • if the array is full (i.e. size == capacity), then:

      1. Allocate new array

      2. Copy objects over (copy constructor)

        • if a copy constructor throws:

          • destroy the new array

          • old array still intact

          • Strong guarantee

      3. Delete old array (no throw)

However,

  • Copying is expensive and the old data will be thrown away

  • Wouldn't moving the obejcts be more efficient?

Move Constructor:

  • if the array is full (i.e. size == capacity), then:

    1. Allocate new array

    2. Move the objects over (move constructor)

      • if move constructor throws, then old array is no longer intact

      • Cannot offer strong guarantee anymore

    3. Delete old array

What if move constructor offers the nothrow guarantee?

  • Emplace_back will use the move constructor

  • Otherwise it will use the copy constructor - this is slower

So your move options should offer the nothrow guarantee, and you should indicate that they do!

Note: pointer op, pointer assignments, delete will never throw

Note: If you know a function will never throw or propogate an exception, declare it noexcept, facilitates optimization. At minimal, moves and swaps should be noexcept

Last updated

Was this helpful?