C++ Workshop ============ --- Contents -------- [Intro](#intro) [Basics](#basics) [STL](#stl) [Memory](#memory) [Classes](#classes) [Templates](#templates) [Exceptions](#exceptions) [Style](#style) --- Intro ===== --- What is C++? (practical level) ----------------------------------- - C-like syntax - _C programs can be compiled as C++_ - Supports Object Oriented Programming - _like structs with functions_ - _C has great performance, but it is missing OO_ - Standard Template Library (STL) - _pre-written classes and algorithms eg._ `std::list` or `std::sort` ---- What is C++? (higher level) ----------------------------------- - High performance - Aims to have Zero Cost Abstractions - Portable - Runs on many devices eg. x86, ARM, Arduino, Comodore64 - Robust - Strong type system & value semantics - RAII (scope) & strict resource ownership - Fairly expressive - How clearly intent can be expressed in code - How closely it follows spoken language ---- Not very expressive ------------------- ```C // C version struct string_elem_t { const char* str_; string_elem_t* next_; }; int num_hamlet(string_elem_t* books) { const char* hamlet = "Hamlet"; int n = 0; string_elem_t* b; for (b = books; b != 0; b = b->next_) if (strcmp(b->str_, hamlet) == 0) ++n; return n; } ``` ---- Fairly expressive ----------------- ```C++ // C++ version int num_hamlet(const std::list<std::string>& books) { return std::count(books.begin(), books.end(), "Hamlet"); } ``` ---- Very expressive --------------- ```python def numHamlet(books): return len(b for b in books if b == 'Hamlet') ``` ---- What is C++? (corollary) ----------------------------------- - Very flexible so can be _unsafe_ if not coded with standards and practices in mind - Highly backwards compatible - As practices have evolved, lots has been added - Added difficulty: knowing which are best current practices - Fair amount of syntax and concepts to learn --- Hello World now in C++ ---------------------- ```cpp #include <iostream> // cout int main() { std::cout << "Hello World\n"; return 0; } ``` :::info Compared to Python, there are `{` curlys `}` and semi-colons `;` ::: ---- Hello World C++ using namespace -------------------------------- ```cpp #include <iostream> // cout using namespace std; // ADDED int main() { cout << "Hello World\n"; // CHANGED: removed `std::` return 0; } ``` :::warning This is discouraged in modern C++ style because it _pollutes the namespace_ ::: --- C++ file extensions ------------------- - `.cpp` for implementation - `.h` or `.hpp` for header files (interface) --- How to compile? --------------- ```shell rez env gcc g++ -Wall -Wextra -pedantic -std=c++14 -O2 -o hello hello.cpp ``` Online compiler here: [wandbox.org](https://wandbox.org) ---- How to compile with clang? --------------- ```shell rez env clang clang++ -Wall -Wextra -pedantic -std=c++14 -O2 -o hello hello.cpp ``` :::info Clang can provide nicer and coloured errors ::: :::warning `g++` and `clang++` binaries may have differences ::: :::info `gcc` and `g++` are aliased to Clang on macOS ::: --- Basics ====== --- Key Terminology --------------- - __Declaration__: _Defining of entity name, type (and perhaps parameters)_ - __Definition__: _Assigning value/instructions to a declared entity_ :::info In Python variables (and their types) don't need to be declared before use, only defined. ::: ---- ```C++ int main() { int number; // declaration (contains undefined random number) number = 42; // definition int countdown = 10; // both at once return 0; } ``` :::warning Best to always do both at once to avoid accidents. ::: :::info Recommended: declare and define variables close to first use. ::: --- Scope ----- - __Scope__: _Part of a program where a name is valid_ - A scope begins from its point of declaration - ...and ends at the end of the block (at the `}`) - 4 types: local, function, namespace, class - Restricting scope helps _reason_ about a piece of code ---- Local Scope ----------- ```cpp // How many local scopes exist here? void foo() { int i = 42; for (int i = 0; i < n; ++i) { // do some stuff } if (i < 53) { // do some stuff } { int i; } } ``` ---- :::danger Try and avoid "_name hiding_" - that is, redeclaring a variable with a name that already exists in local scope. ::: ---- Local Scope ----------- ```cpp // How many local scopes exist here? void foo() { if (true) { int i = 42; else { int i = 53; } std::cout << i << '\n'; // ERROR - no i in scope } ``` :::info This is different from python which has function scope (and is _garbage collected_) ::: ---- Function Scope -------------- Prototype declarations and definitions ```cpp void swap(int a, int b); // prototype int main() { int a = 42; int b = 53; swap(a, b); // will this work? } void swap(int a, int b) { int temp = a; a = b; b = temp; } ``` ---- Namespace Scope --------------- ```cpp namespace Fruit { std::string name; } namespace Vegetable { std::string name; } void baz() { std::cout << Fruit::name; std::cout << Vegetable::name; std::cout << name; // error } ``` ---- Class Scope ----------- See the _[Classes](#classes)_ section --- `const` ----- ```cpp int a; // normal integer. const int b; // integer that can't be modified. const int* c; // pointer to a const integer. // The value "c" points to cannot be modified. int* const d; // const pointer to an integer. // "d" cannot be modified to point elsewhere. const int* const e; // const pointer to a const integer. // The value "e" points to cannot be modified. // "e" cannot be modified to point elsewhere. ``` - The trick is to read it backwards - "`e` is a `const` _pointer_ to an `int` that is `const`" ---- ```cpp int initial = 42; int changeTo = 1; const int b = 0; b = 3; // ERROR const int* c = &initial; *c = changeTo; // ERROR c = &changeTo; // ok ``` - `*` after type means _pointer to a variable of that type_ - `*` before variable use means _dereference_ - "_go to and use the variable at this address_" ---- ```cpp int initial = 42; int changeTo = 1; int* const d = &initial; *d = changeTo; // ok d = &changeTo; // ERROR const int* const e = &initial; *e = changeTo; // ERROR e = &changeTo; // ERROR ``` --- Casting (Explicit Type Conversion) ------- - Implicit type conversions happen all the time. ```cpp double s = 2; // an integer upcast to a double // s == 2.0 int t = 2.5; // a double downcast to an int // t == 2 ``` - However sometimes implicit casts don't work. ---- - What is wrong with below? - What is the value of result? _C-style cast_ ```c double result = rand()/(double)RAND_MAX; ``` :::warning C-style casts are hard to "search", and generalise different kinds of casting. <!-- .element: class="fragment" data-fragment-index="1" --> ::: ---- `static_cast<T>` -------------- Allows for value-casting for related types. ```cpp double result = rand()/static_cast<double>(RAND_MAX); ``` :::info Static casting occurs at compile time. This reduces odds of runtime bugs. ::: ---- `reinterpret_cast<T>` ------------------- Keeps all the bits the same, and gives it a new label. ```cpp void* myPointer = 0x3894AEBB int myInteger = static_cast<int>(myPointer); // Not related, fail int myInteger = reinterpret_cast<int>(myPointer); // This succeeds, as myInteger is the integer value of the // address of the myPointer pointer int myInteger = (int)myPointer; // same as re-interpret cast ``` :::info Reinterpret Cast is what C-style casts "fall back" on / assumed to do. ::: ---- `const_cast<T>` ------------- Removes the `const`-ness of a type ```cpp const int light_intensity = 300; const_cast<int>(light_intensity) = 400; ``` :::danger This should be used with CAUTION. Things are usually const for a reason! ::: --- References ---------- Let's start with a basic example: ```cpp int i = 1; int& myref = i; // The following have the same effect i = 2; myref = 2; ``` :::info `myref` is just another name for object `i` ::: - `&` after a type means "_reference_" - `&` before variable (when there is no type) means "_address of_" ---- References - Key Points ----------------------- - References <b>must</b> be defined when declared - References are always const (since it can't be redefined) ```cpp // non-const reference: Reference to non-const object int i = 3; int &nonconstref = i; // const reference: Reference to const object const int i = 3; int &constref = i; ``` :::success References are safer because they can NEVER be null ::: --- STL === --- Standard Template Library ------------------------- > Provides data-structures and algorithms so you don't have to reinvent the wheel :::info Everything in STL is in the `std` namespace ::: --- Browsing Documentation -------------------------- - [cppreference.com](http://en.cppreference.com/w/cpp) - [devdocs.io](https://devdocs.io/cpp/) ---- Documentation Viewer: Zeal (linux) ---------------------------------- ### [zealdocs.org](https://zealdocs.org) ![screenshot](https://i.imgur.com/v63u1ZJ.png) - offline mode - fuzzy search - free & open source ---- Documentation Viewer: Dash (macOS) ---------------------------------- ### [kapeli.com/dash](https://kapeli.com/dash) ![](https://i.imgur.com/EHOOVSl.png) - offline mode - fuzzy search - not free :disappointed: (but has limited free version) ---- Documentation Viewer: Velocity (windows) ---------------------------------------- ### [velocity.silverlakesoftware.com](https://velocity.silverlakesoftware.com) - offline mode - fuzzy search - free --- `#include`-ing libraries ---------------------- - C++ builtin header files don't need a `.h` - `#include <list>` - Old C libraries are available, prefix with a `c` - `#include <cstdlib>` - `#include <cassert>` - Your own header files still need the extension - `#include "utils/Timer.hpp"` - `#include <boost/algorithm/string.hpp>` --- STL Containers -------------- - __[vector](#stdvector)__ _a dynamic array_ - __[list](#stdlist)__ _a linked list_ - __stack__ _first in, last out_ - __queue__ _first in, first out_ - __priority_queue__ _an always sorted queue_ - __deque__ _a queue and also a stack_ - __set__ _an ordered set (red-black tree)_ - __map__ _(key : value) store - like `dict` in python_ --- `std::string` ------------- - `std::string` abstracts away (C-like) `char` arrays. ```cpp #include <iostream> #include <string> int main() { std::string greeting = "Hello there!"; std::cout << greeting << '\n'; return 0; } ``` ---- ```cpp #include <iostream> #include <string> int main() { using std::string; string greeting = "Hello there!"; std::cout << greeting << '\n'; return 0; } ``` ---- _fruit.hpp_ ```cpp const std::string fruits[] = { "Apple", "Pear", "Orange", "Banana", //... "Mango", "Apricot", }; ``` --- `std::list` ----------- - `std::list` is a generic linked list implementation ```cpp #include <list> #include <string> int main() { using std::list; int numbers[] = {2, 3, 5, 7}; list<int> primes = list<int>(numbers, numbers + 4); // not good practice int names[] = {"BB8", "Chewie", "Han", "R2D2"}; list<std::string> characters = list<std::string>(); for (const auto& name : names) { characters.push_back(name); characters.push_back("Rey"); characters.push_front("Yoda"); return 0; } ``` ---- :::warning Avoid pointer arithmetic (`numbers`) STL Arrays are preferred because they store the size ::: --- Iterating through an STL container ---------------------------------- ![](https://i.imgur.com/FCcDfN4.png) ---- _ObservedPostHistory.cpp_ ```cpp void ObservedPostsHistory::printHistory() const { using std::list; list<ObservedPostSide>::const_iterator it = posts_.begin(); std::cout << "history = "; while (it != posts_.end()) { std::cout << *it << " "; ++it; } std::cout << std::endl; } ``` ---- Variation using a `for` loop _ObservedPostHistory.cpp_ ```cpp void ObservedPostsHistory::printHistory() const { std::cout << "history = "; std::list<ObservedPostSide>::const_iterator it; for (it = posts_.begin(); it != posts_.end(); ++it) { std::cout << *it << " "; } std::cout << "\n"; } ``` :::success With a `for` loop you can't forget to increment ::: ---- Rewritten using the `std::copy` algorithm _ObservedPostHistory.cpp_ ```cpp void ObservedPostsHistory::printHistory() const { std::cout << "history = "; std::copy(posts_.cbegin(), posts_.cend(), std::ostream_iterator<ObservedPostSide>(std::cout, " ")); std::cout << "\n"; } ``` :::success With algorithms incrementing is hidden - you can't accidentally read an invalid index. ::: --- `std::vector` ------------- - A dynamic array implementation - It resizes for you! ```cpp int main() { std::string tmp[] = {"don't", "push", "me", "I'm", "close", "to", "the", "edge"}; std::vector<std::string> the_messages; for (int i = 0; i < 8; ++i) { the_messages.push_back(tmp[i]); } } ``` --- STL Algorithms -------------- - STL Algorithms take container iterators (`.begin()` and `.end()`) - Complete list at [cppreference.com/w/cpp/algorithm](http://en.cppreference.com/w/cpp/algorithm) --- `std::find` ----------- - Returns an iterator (pointer) pointing to the element if found, otherwise the `.end()` ```cpp int main() { using namespace std; std::vector<string>::const_iterator result; string tmp[] = {"hay", "hay", "hay", "needle", "hay", "hay"}; vector<string> h_stack = vector<string>(tmp, tmp + 6); result = find(h_stack.cbegin(), h_stack.cend(), "needle"); if (result != h_stack.end()) { cout << *result << "\n"; } return 0; } ``` --- `std::sort` ----------- - Sorts elements in a container _MultiGaussianDistribution.cpp_ ```cpp void MultiGaussianDistribution::fixupDistribution(void) { std::sort(modes.begin(), modes.end(), GuassianSortFunction()); normaliseDistribution(modes); mergeSimilarModes(modes); std::sort(modes.begin(), modes.end(), GuassianSortFunction()); normaliseDistribution(modes); removeUnlikelyModes(modes); removeExcessModes(modes, maxGaussians); normaliseDistribution(modes); } ``` --- `std::for_each` --------------- - Maps a function to a range of elements ```cpp void greySky() { void makeBrown(Leaf l) { l.setColor("brown"); } std::vector<Leaf> leaves = std::vector<Leaf>(66, Leaf()); std::for_each(leaves.begin(), leaves.end(), makeBrown()); } ``` --- Boost ----- - extended libraries in the `boost::` namespace - documentation at [boost.org](http://www.boost.org/doc/libs/1_62_0/libs/libraries.htm) is your friend - also [theboostcpplibraries.com](https://theboostcpplibraries.com/the-boost-c++-libraries) ---- Boost Example: splitting on whitespace ```cpp #include <boost/algorithm/string.hpp> // split, is_space // ...other #includes int main() { std::string text; std::vector<std::string> results; std::cout << "Enter some text to split:" << '\n'; std::getline(std::cin, text); boost::split(results, text, boost::is_space()); for (size_t i = 0; i < results.size(); ++i) { std::cout << results[i] << '\n'; } } ``` --- Memory ====== --- Stack and Heap memory --------------------- - __Stack__: _Named Memory_ - __Heap__: _Unnamed Memory_ ---- Stack Memory ------------ ```cpp int doFoveaThings() { Fovea myFovea; myFovea.setPixel(1, 1); myFovea.getPixel(1, 1); // ~myFovea() is called } ``` ---- Heap Memory (`new` and `delete`) -------------------------------- ```cpp int doFoveaThings() { Fovea* myFovea = new Fovea(); myFovea->getPixel(1, 1); // memory leak :O :/ :( } ``` :::warning It is strongly recommended to use [smart pointers](https://youtu.be/UOB7-B2MfwA) instead of `new` and `delete` because they have ownership control (safer) ::: ---- Heap Memory ------------ ```cpp int doFoveaThings() { Fovea* myFovea = new Fovea(); myFovea->setPixel(1, 1); myFovea->getPixel(1, 1); delete myFovea; } ``` ---- Memory with array types ----------------------- ```cpp int doFoveaThings() { Fovea myFoveaList[5]; myFovea[0]->setPixel(1, 1); myFovea[0]->getPixel(1, 1); } ``` ```cpp int doFoveaThings() { Fovea* myFovea = new Fovea[5]; myFovea[0].setPixel(1, 1); myFovea[0].getPixel(1, 1); delete[] myFovea; } ``` --- Classes ======= - Classes allow for Object Oriented programming. ---- - We want to create a Robot class. In C we might have: _Robot.c_ ```c struct Robot { int serial_number; float version; }; ``` ---- - To make it a class, just replace `struct` with `class`. This makes everything `private`. _Robot.cpp_ ```cpp class Robot { int serial_number; float version; }; ``` ---- - Let's add legs. `Leg` is another class that we will write. _Robot.cpp_ ```cpp class Robot { Leg left; Leg right; int serial_number; float version; }; ``` :::warning Note the semicolon. `g++` will complain without it. ::: ---- - In C we could _abstract_ (hide) the implementation. In C++ we use `public` and `private`. - We also made a public _method_ called walk. _Robot.cpp_ ```cpp class Robot { public: void walk(int steps) { for (int i = 0; i < steps; ++i) { left.step(); right.step(); } } private: Leg left; Leg right; int serial_number; float version; }; ``` ---- ```cpp int main() { Robot bb8 = Robot{}; // uniform initialisation on the stack while (true) { bb8.walk(4); } // can only access public methods from here return 0; } ``` ---- - We can put _method declarations_ in the class body and put the _definitions_ outside. - This makes it easier to read the class interface. _Robot.cpp_ ```cpp class Robot { public: void walk(int steps); private: Leg left; Leg right; int serial_number; float version; }; void Robot::walk(int steps) { for (int i = 0; i < steps; ++i) { left.step(); right.step(); } }; ``` ---- - If we do that, we can seperate the code, putting the interface in a `.h` header file. _Robot.h_ ```cpp class Robot { public: void walk(int steps); private: Leg left; Leg right; int serial_number; float version; }; ``` ---- - Type goes first, then the name of the class (like a namespace). _Robot.cpp_ ```cpp void Robot::walk(int steps) { for (int i = 0; i < steps; ++i) { left.step(); right.step(); } }; ``` ---- _Leg.h_ ```cpp class Leg { public: void move(); private: Sensor toe_bumper; Motor ankle; }; ``` --- Inheritance ----------- _base class_ ```cpp class Cake { protected: int radius_; // in cm void bake() { baked_ = true; } void applyIcing() { hasIcing_ = true; } public: Cake() { radus_ = 20; has_icing_ = false; } virtual void cook { bake(); applyIcing(); } privatej bool hasIcing_; bool baked_; }; ``` ---- _sub class_ ```cpp class CupCake : public Cake { public: CupCake() { radius_ = 5; // can modify because it is "protected" } virtual void cook() { bake(); applyIcing(); } }; ``` ---- _sub sub class_ ```cpp class BlueberryCupCake : public CupCake { public: CupCake() { radius_ = 5; // can modify because it is "protected" } void addBlueberries(int n_blueberries) { for (int i = 0; i < n_candles; ++i) { blueberries_.push_back(FreshBlueberry()); // Polymorphism: LSP } } virtual void cook() { addBlueberries(); bake(); applyIcing(); } private: std::vector<Blueberry> blueberries_; }; ``` ---- _sub class_ ```cpp class BirthdayCake : public Cake { public: BirthdayCake() { radius_ = 25; // can modify because it is "protected" } virtual void cook() { bake(); applyIcing(); addCandles(); } void addCandles(int n_candles) { for (int i = 0; i < n_candles; ++i) { candles_.push_back(Candle()); } } private: std::vector<Candle> candles_; }; ``` --- Subtype Polymorphism -------------------- - __Liskov Substitution Principle__: _Every sub class should be substitutable for its base class_. - [more info on OO Design Principles](https://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principles-of-object-oriented-design) - __Contravariance__: _Parameters of overriding functions should be the same or a sub class_. - __Covariance__: _Return type of the overriding function should be the same or a parent class_. ---- ```cpp class Person { public: void makeAndEatCake(Cake c) { c.cook(); isFull = true; } private: bool isFull; } ``` ```cpp int main() { Person brad = Person(); CupCake c = CupCake(); brad.makeAndEatCake(c); } ``` ---- - Sub class `CupCake` is substitutable where base class `Cake` is expected. - Think of it as the sub class has some extra stuff (member variables and methods) - The subclass has all the base class stuff too - Anything you can do to the subclass, you can also do to the base class. --- Templates ========= --- Generic Programming ------------------- Generalising software to be independent of type (e.g. `int`, `double`) STL is an exmaple of generic programming `vector<int>` --- Function Templates - Why? ------------------ ```cpp int min(int a, int b) { if (a < b) { return a; } return b; } ``` ```cpp double min(double a, double b) { // a function overload if (a < b) { return a; } return b; } ``` ---- We can replace that with: ```cpp template <typename T> T min(T a, T b) { if (a < b) { return a; } return b; } ``` ```cpp int main() { int i1 = 1; int i2 = 2; double d1 = 1.0; double d2 = 2.0; std::cout << min<int>(i1,i2); std::cout << min<double>(d1, d2); } ``` ---- What is wrong here ```cpp int main() { int i1 = 1; int i2 = 2; double d1 = 1.0; double d2 = 2.0; std::cout << min<int>(i1, d2); } ``` ---- What is wrong here? ```cpp template <typename U,V,W> W min(U a, W b) { if (a < b) { return a }; return b; } ``` --- Partial Specialisation ---------------------- - Pointers are different types, so we need to redefine our generic function or class for them as well. ```cpp template <typename T> T* min(T* a, T* b) { if (*a < *b) { return *a; } return *b; } ``` ```cpp int main() { int* i1 = 1; int* i2 = 2; double* d1 = 1.0; double* d2 = 2.0; std::cout << min(i1,i2); std::cout << min(d1, d2); } ``` --- Explicit Specialisation ----------------------- - With every rule there is exceptions. Explicit specialisation is how we make these exceptions. - Let's assume we want to re-define how the minimum string is found. ---- ```cpp // Define minimum not as least alphabetically, but as minimum number of characters template <typename T> std::string min(std::string a, std::string b) { if (a.size() < b.size()) { return a; } else { return b; } } ``` ```cpp int main() { std::string s1 = "Apple"; std::string s2 = "Zoo"; std::cout << min(s1, s2); } ``` --- Class Templates --------------- ```cpp template <typename T> class SubImage { public: SubImage(int width, int height); T getPixel(int row, int column); void setPixel(int row, int column, T); int getHorizontalCount(); int getVerticalCount(); private: vector<vector<T> > pixels; int horizontal_start; int horizontal_end int vertical_start; int vertical_end; } ``` ---- ```cpp int main() { Rgb examplePixel(255,255,255); SubImage* potentialBall = new SubImage(100,100); potentialBall.setPixel(0,0,examplePixel); } ``` --- Exceptions ========== --- Exception --------- - __Exception__: A run-time anomoly (a runtime error!) - __Exception-handling__: Run-time mechanism to communiate exceptions between two unrelated parts of code --- `try`/`catch` --------- ```cpp int input; try { if (input > 100) { throw 100; } } catch (100) { // Caught - print out error } // Code continues ``` If an exception is thrown and not caught, the program terminates (this can be ok). :::warning It is good for errors to be visible and loud, rather than silently failing ::: ---- Types of throws --------------- You can throw any type in C++, whether it is a primitive type: ```cpp int input; try { if (input > 100) { throw 100; } } catch (int& i) { // Caught - print out error } // Code continues ``` ---- Types of throws --------------- Or a class object ```cpp int input; try { if (input > 100) { throw new RuntimeException; } } catch (RuntimeException& e) { // Caught - print out error std::cout << "Error: " << e << std::endl; } // Code continues ``` --- Exception Hierarchy ------------------- - Exceptions allow for the use of Polymorphism. - For example if BallDetectionError and GoalDetectionError are a sub-class of VisionError: ```cpp try { if (/*something wrong with ball*/) { throw new BallDetectionError(); } if (/*something wrong with*/) { throw new GoalDetectionError(); } } catch (VisionError& e) { // Catch any vision-based error } // Code continues ``` ---- Catching anything, `"..."` -------------------------- - Sometimes we want to catch ANYTHING that can be thrown ```cpp int input; try // throw anything, doesn't matter what it is } catch (...) { // Catch anything - print out error } // Code continues ``` --- Rethrow ------- - After we catch a thrown exception, and we "clean up" sometimes we want to throw it again to the layer above. ```cpp template <typename T> class Image2D { public: Image2D(std::string name) { this.name = name }; T& getItem(int row, int column); std::string& getName(); private: vector<vector<T> > v; vector<std::pair<int,int> > failedIndexLookup; std::string name; } ``` ---- ```cpp Image2D::getItem(int row, int column) { try { T item = this.v.get(row).get(column); } catch (std::out_of_range& e) { this.failedIndexLookup.push_back(std::make_pair(row,column)); throw; } } void foo() { Image2D<Fovea> nvf; try { nvf.get(1,2); } catch (std::logic_error) { std::cerr << "Logic error in lookup" << std::endl; } } ``` ---- ```cpp Image2D::getItem(int index) { try { T item = this.v.get(index); } catch (std::out_of_range& e) { this.failedIndexLookup.push_back(index); throw; } catch (...) { throw; } } ``` --- Preventing Memory Leaks ----------------------- ```cpp Image2D::getItem(int index) { int* modifiedIndex = new int; *modifiedIndex = index - FRAME_OFFSET; try { T item = this.v.get(modifiedIndex); } catch (std::out_of_range& e) { this.failedIndexLookup.push_back(modifiedIndex); throw; } catch (...) { throw; } delete modifiedIndex; } ``` --- Preventing Memory Leaks ----------------------- ```cpp Image2D::getItem(int index) { int* modifiedIndex = new int; *modifiedIndex = index - FRAME_OFFSET; try { T item = this.v.get(modifiedIndex); } catch (std::out_of_range& e) { this.failedIndexLookup.push_back(modifiedIndex); delete modifiedIndex; throw; } catch (...) { delete modifiedIndex; throw; } delete modifiedIndex; } ``` :::warning Remember to free all heap memory you've allocated in your catch statements ::: --- That's all folks!