Intermediate C++ 1/83

Similar documents
QUIZ. What is wrong with this code that uses default arguments?

Homework 4. Any questions?

Part X. Advanced C ++

use static size for this buffer

Overview. 1. Expression Value Categories 2. Rvalue References 3. Templates 4. Miscellaneous Hilarity 2/43

Fast Introduction to Object Oriented Programming and C++

Database Systems on Modern CPU Architectures

CSE 333 Lecture smart pointers

CSE 333 Lecture smart pointers

The issues. Programming in C++ Common storage modes. Static storage in C++ Session 8 Memory Management

Programming in C and C++

Object-Oriented Programming for Scientific Computing

RAII and Smart Pointers. Ali Malik

COMP6771 Advanced C++ Programming

A brief introduction to C++

CS 241 Honors Memory

CSE 333. Lecture 11 - constructor insanity. Hal Perkins Paul G. Allen School of Computer Science & Engineering University of Washington

COMP6771 Advanced C++ Programming

POLYMORPHISM 2 PART Abstract Classes Static and Dynamic Casting Common Programming Errors

CS

POLYMORPHISM 2 PART. Shared Interface. Discussions. Abstract Base Classes. Abstract Base Classes and Pure Virtual Methods EXAMPLE

Overload Resolution. Ansel Sermersheim & Barbara Geller Amsterdam C++ Group March 2019

Overload Resolution. Ansel Sermersheim & Barbara Geller ACCU / C++ June 2018

Vector and Free Store (Pointers and Memory Allocation)

ECE 449 OOP and Computer Simulation Lecture 14 Final Exam Review

Advanced C++ Topics. Alexander Warg, 2017

2 ADT Programming User-defined abstract data types

SERIOUS ABOUT SOFTWARE. Qt Core features. Timo Strömmer, May 26,

ECE 449 OOP and Computer Simulation Lecture 12 Resource Management II

CS 251 INTERMEDIATE SOFTWARE DESIGN SPRING C ++ Basics Review part 2 Auto pointer, templates, STL algorithms

1/29/2011 AUTO POINTER (AUTO_PTR) INTERMEDIATE SOFTWARE DESIGN SPRING delete ptr might not happen memory leak!

CE221 Programming in C++ Part 1 Introduction

CS11 Advanced C++ Fall Lecture 7

PIC 10A Objects/Classes

CSCI-1200 Data Structures Fall 2011 Lecture 24 Garbage Collection & Smart Pointers

A Crash Course in (Some of) Modern C++

C++ Programming: Polymorphism

C++11 and Compiler Update

Introduction to C++ Professor Hugh C. Lauer CS-2303, System Programming Concepts

PHY4321 Summary Notes

Ch. 12: Operator Overloading

Part III. Advanced C ++

An Introduction to C++

CS3157: Advanced Programming. Outline

Introduction to C++ Systems Programming

A Generic Non-intrusive Smart Pointer Implementation

Programmazione. Prof. Marco Bertini

Smart Pointers. Some slides from Internet

These new operators are intended to remove some of the holes in the C type system introduced by the old C-style casts.

6 Architecture of C++ programs

See the CS 2704 notes on C++ Class Basics for more details and examples. Data Structures & OO Development I

COMP322 - Introduction to C++ Lecture 10 - Overloading Operators and Exceptions

A brief introduction to C programming for Java programmers

Engineering Robust Server Software

AIMS Embedded Systems Programming MT 2017

Software Engineering Concepts: Invariants Silently Written & Called Functions Simple Class Example

Software Design and Analysis for Engineers

Midterm Review. PIC 10B Spring 2018

Lecture 2, September 4

CSE 374 Programming Concepts & Tools

377 Student Guide to C++

Short Notes of CS201

CS 231 Data Structures and Algorithms, Fall 2016

nptr = new int; // assigns valid address_of_int value to nptr std::cin >> n; // assigns valid int value to n

G52CPP C++ Programming Lecture 15

G52CPP C++ Programming Lecture 20

QUIZ. Write the following for the class Bar: Default constructor Constructor Copy-constructor Overloaded assignment oper. Is a destructor needed?

CSCI-1200 Data Structures Spring 2017 Lecture 27 Garbage Collection & Smart Pointers

CSE 374 Programming Concepts & Tools. Hal Perkins Fall 2015 Lecture 19 Introduction to C++

Arrays. Returning arrays Pointers Dynamic arrays Smart pointers Vectors

CS201 - Introduction to Programming Glossary By

These are notes for the third lecture; if statements and loops.

Chapter 13: Copy Control. Overview. Overview. Overview

Introduction to Programming Using Java (98-388)

Appendix. Grammar. A.1 Introduction. A.2 Keywords. There is no worse danger for a teacher than to teach words instead of things.

Client Code - the code that uses the classes under discussion. Coupling - code in one module depends on code in another module

Introduction to C++ Introduction to C++ 1

OBJECT ORIENTED PROGRAMMING USING C++

Chapter 15 - C++ As A "Better C"

Looping and Counting. Lecture 3 Hartmut Kaiser hkaiser/fall_2012/csc1254.html

Copyie Elesion from the C++11 mass. 9 Sep 2016 Pete Williamson

eingebetteter Systeme

Advanced Systems Programming

GEA 2017, Week 4. February 21, 2017

Looping and Counting. Lecture 3. Hartmut Kaiser hkaiser/fall_2011/csc1254.html

CSE 374 Programming Concepts & Tools. Hal Perkins Spring 2010

COEN244: Class & function templates

C++11: 10 Features You Should be Using. Gordon R&D Runtime Engineer Codeplay Software Ltd.

CSE 333 Lecture 9 - intro to C++

04-24/26 Discussion Notes

Pointers and References

What are the characteristics of Object Oriented programming language?

C++ without Classes. CMSC433, Fall 2001 Programming Language Technology and Paradigms. More C++ without Classes. Project 1. new/delete.

Instantiation of Template class

Object-Oriented Programming for Scientific Computing

Intermediate Programming, Spring 2017*

CS11 Advanced C++ Spring 2018 Lecture 2

Cpt S 122 Data Structures. Course Review Midterm Exam # 2

Exception Namespaces C Interoperability Templates. More C++ David Chisnall. March 17, 2011

Chapter 17 vector and Free Store

Transcription:

Intermediate C++ 1/83

Sections I. Memory Management Basics II. The C++ Standard Library III. Casting IV. Resource Management: RAII 2/83

I. Memory Management Basics 1. Checking for memory leaks 2. Pass by value 3. Pass by reference 4. Const 3/83

Preliminary notes - For the purpose of brevity, we shall refer to objects and primitive types collectively as just objects - If you see information in a box like this one, it means this information is helpful to understand, but not necessary for the course 4/83

1. Checking for memory leaks - Remember new and delete? - Failing to delete can cause memory leaks! - Valgrind: Tool used to check code for memory leaks - Run from Qt s Analyze drop-down menu - Tells you where leaked memory was allocated - Check your code for leaks! - Leaks will slow down your program and eventually crash it - The TAs will deduct heavily for leaks 5/83

2. Pass by value - Functions often take arguments - They have a number of ways of receiving those arguments - These differences are important - Let s look at the first: Pass by value 6/83

// main.cpp void increaseandprintunicorn(unicorn uni) { // 3. Take a Unicorn as an argument uni.increaseval(); // 4. Call some member function. uni.printval(); // 5. Output: 6 } int main(int argc, char* argv[]) { Unicorn u(5); // 1. Create a Unicorn with initial value of 5. increaseandprintunicorn(u); // 2. Pass the Unicorn by value. This invokes the Unicorn s copy constructor u.printval(); // 6. Output: 5 } // Syntax note: Use period (.) to access class members of objects. Use arrow (->) when dealing with a pointer to an object. // Bonus note: The dereference operator (->) can be overridden (operator overloading is a thing in C++). The structure reference // operator (.) cannot be overridden. 7/83

2. Pass by value cont. - Passing by value creates a copy of an object to give to the function - If you do stuff to a copy of an object - You re not changing the original one! - Remember this! - But what can we notice about this example? - Hint: Something inefficient is happening... 8/83

// main.cpp void increaseandprintunicorn(unicorn uni) { // 3. This Unicorn is constructed as main s Unicorn is copied uni.increaseval(); uni.printval(); } // 5. Call Unicorn s destructor for uni. int main(int argc, char* argv[]) { Unicorn u(5); // 1. Create a Unicorn with initial value of 5. increaseandprintunicorn(u); // 2. Pass the Unicorn by value. This invokes the Unicorn s copy constructor u.printval(); } // Call Unicorn s destructor for u. 9/83

2. Pass by value cont. - A Unicorn object was constructed and destroyed twice - This is inefficient - Imagine passing an object through several layers of function calls - it just gets worse and worse! - Big objects will take even more time and memory - This is Not A Good Thing - What do? 10/83

3. Pass by reference - A reference is, well, a reference to an object - A reference is like another name for the object - It refers to the same object - It is not a pointer - References are denoted with the & symbol 1 - Passing by reference is A Good Thing 1 The technical name of which is the lvalue reference declarator 11 /83

// main.cpp void increaseandprintunicorn(unicorn &uni) { // 3. This function takes a reference to a Unicorn. No constructor called here // Unlike before, this is not a copy of main s Unicorn, it is main s Unicorn. uni.increaseval(); uni.printval(); // Output: 6 } // 4. No destructor called here int main(int argc, char* argv[]) { Unicorn u(5); // 1. Create a Unicorn with initial value of 5. increaseandprintunicorn( u ); // 2. Pass the Unicorn by reference. This does not invoke Unicorn s copy constructor u.printval(); // 5. Output: 6 } // Call Unicorn s destructor for u now that it is going out of scope 12/83

3. Pass by reference cont. - Two important changes - No copy and destruction of a new Unicorn - The Unicorn passed in can be modified - Question: What if we want to efficiently pass an object by reference, but don t want it to be modified? 13/83

4. Const - Keyword const is used to signal that something will not or cannot change - Pass by const reference is the solution to the last problem and is A Good Thing - Prefer pass by const reference unless - You are passing a very small argument (e.g. int) - You want to modify the argument 14/83

// main.cpp void changeunicorn(unicorn &uni) { // Non-const ref = can change the argument } uni.setval(7); void printunicorn(const Unicorn &uni) { } uni.printval(); // Const ref = can t change the argument // wum does not change int main(int argc, char* argv[]) { Unicorn u(5); // Initialize u to 5 changeunicorn(u); // Do something to u printunicorn(u); // Output: 7 } 15/83

// main.cpp void changeunicorn(const Unicorn &uni) { } uni.setval(7); // This can t be right. Change a const Unicorn? // Error: Unicorn uni is const, can t change the value // Either don t change u or remove the const qualifier void printunicorn(const Unicorn &uni) { } uni.printval(); // uni does not change. As expected. Nothing to see here. int main(int argc, char* argv[]) { Unicorn u(5); // Initialize u to 5 changeunicorn(u); // Do something to u printunicorn(u); // Output: None because this code doesn t compile } 16/83

4. Const cont. - Classes can use keyword const as well - Const member variables can t be changed - Const member functions are very useful - They signal that the object s state will not change when the function is called 17/83

// unicorn.h class Unicorn { public: Unicorn(int x); int getx() const; // A const member function declaration private: int m_x; } // unicorn.cpp #include unicorn.h Unicorn::Unicorn(int x) : {} m_x(x) int Unicorn::getX() const { } return m_x; // A const member function definition. Nothing about Unicorn changes 18/83

II. The C++ Standard Library 1. Overview 2. Templates 3. Case Study: std::map 4. Iterators 5. Other Container Classes 19/83

1. Overview - The C++ Standard Library is a collection of useful classes provided with C++ - They re not just standard issue tools - They re literally part of the C++ Standard 20/83

1. Overview cont. - The Standard Library is A Good Thing - Check to see if the Standard Library has what you need before you try to make it yourself - Theirs is better - Unless you re working in a very specific high-performance and/or nonstandard environment - Which you re not 21/83

1. Overview cont. - What s in the Standard Library? - std::string - words - std::vector - dynamic array - std::map - key/value map - std::cout, std::cin, std::cerr - standard i/o - <cmath> header includes many common math functions 22/83

2. Templates - C++ templates are used for generic programming - They re similar to Java Generics - In 123 you will never need to create templates so we will only go over how to use them in the context of the Standard Library - See the Advanced C++ slides for more 23/83

// main.cpp #include <vector> #include Unicorn.h // Include the vector header // A unicorn class int main(int argc, char *argv[]) { std::vector<unicorn> myunicorns; for (int i = 0; i < 10; i++) { Unicorn u(i); myunicorns.push_back(f); } for (size_t i = 0; i < 10; i++) { myunicorns[i].print(); } } // Make a vector of Unicorns. Note that we could put any type inside the angle // brackets and the vector would behave the same way // Some constructor for a Unicorn // Add the Unicorns to the vector // Iterate through the vector s contents // Call some function on each Unicorn. 24/83

// main.cpp #include <vector> #include Horn.h // Some Horn class instead int main(int argc, char *argv[]) { std::vector<horn> myhorns; // Unicorn or Horn for (int i = 0; i < 10; i++) { Horn h(i); myhorns.push_back(b); } } for (size_t i = 0; i < 10; i++) { } myhorns[i].bang(); 25/83

2. Templates cont. - In general, templates are used for making containers - data structures that don t care about what s inside them - There are more uses, but we won t go over them here - See the Advanced C++ Slides for cool things like template metaprograming! 26/83

3. Case Study: std::map - std::map is a homogeneous map between key objects and value objects - That is, all the keys are all of type K and the values are all of type V - K and V may be different types or the same type - Sort of like a hashmap in Java although std::map is actually a tree. std::unordered_map is actually a hashmap - We ll go over how to use it in C++ 27/83

// main.cpp #include <map> #include <string> // Include appropriate headers int main(int argc, char *argv[]) { std::map<std::string, int> mycolors; // Map from strings to ints mycolors[ Red ] = 200; mycolors[ Green ] = 150; mycolors[ Blue ] = 25.2f; // Store the key and value via the []= operator // Implicit conversion warning - converts float to int } std::cout << Red: << mycolors[ Red ] << std::endl; // Access values the same way you stored them std::cout << Green: << mycolors[ Green ] << std::endl; std::cout << Blue: << mycolors[ Blue ] << std::endl; // Output: // Red: 200 // Green: 150 // Blue: 25 28/83

3. Case Study: std::map cont. - So far so good - But...what about const? 29/83

3. Case Study: std::map cont. - What gives? - The bracket operator isn t const - It returns a non-const reference - You may look at and change the value - However, at() is const 1 - It returns a const reference - Read-only 1 Technically, a const-qualified overload of at() is provided. There is also a non-const-qualified at() function that works like the bracket operator. The cv-qualification (const-volatile qualification) of the object determines which is called. 30/83

int printmapval(const std::map<std::string, int> &mycolors, std::string key) { std::cout << key << : << mycolors[key] << std::endl; // Error: mycolors is const } int printmapval(const std::map<std::string, int> &mycolors, std::string key) { std::cout << key << : << mycolors.at(key) << std::endl; // This is fine } 31/83

3. Case Study: std::map cont. - std::map also comes with some other basic functions - empty() - whether the map is empty or not - clear() - clears all key-value pairs from the map - size() - number of elements stored in the map 32/83

4. Iterators - Iterators are special objects used by container classes to iterate over the container s contents 33/83

// main.cpp #include <map> // other headers omitted int main(int argc, char *argv[]) { std::map<std::string, int> mycolors; // Notice that this type matches the iterator below. They must match mycolors[ Red ] = 100; mycolors[ Green ] = 200; mycolors[ Blue ] = 400; std::cout << Contents of mycolors map << std::endl } // This is the type of your map. It matches the above definition exactly for (std::map<std::string, int>::iterator it = mycolors.begin(); it!= mycolors.end(); it++) { std::cout << it->first << : << it->second << std::endl; } 34/83

// main.cpp #include <map> // other headers omitted int main(int argc, char *argv[]) { std::map<std::string, int> mycolors; // Notice that this type matches the iterator below. They must match mycolors[ Red ] = 100; mycolors[ Green ] = 200; mycolors[ Blue ] = 400; std::cout << Contents of mycolors map << std::endl } // This is the type of your map. It matches the above definition exactly // Access the class s iterator type. It s part of the class, not the instance, so it s accessed via the scope operator for (std::map<std::string, int>::iterator it = mycolors.begin(); it!= mycolors.end(); it++) { std::cout << it->first << : << it->second << std::endl; } 35/83

// main.cpp #include <map> // other headers omitted int main(int argc, char *argv[]) { std::map<std::string, int> mycolors; // Notice that this type matches the iterator below. They must match mycolors[ Red ] = 100; mycolors[ Green ] = 200; mycolors[ Blue ] = 400; std::cout << Contents of mycolors map << std::endl } // This is the type of your map. It matches the above definition exactly // Access the class s iterator type. It s part of the class, not the instance, so it s accessed via the scope operator // std::map has functions that return markers for the beginning and end of your container. // The actual order of the elements is unimportant - the point is that you can loop through all of them for (std::map<std::string, int>::iterator it = mycolors.begin(); it!= mycolors.end(); it++) { std::cout << it->first << : << it->second << std::endl; } 36/83

// main.cpp #include <map> // other headers omitted int main(int argc, char *argv[]) { std::map<std::string, int> mycolors; // Notice that this type matches the iterator below. They must match mycolors[ Red ] = 100; mycolors[ Green ] = 200; mycolors[ Blue ] = 400; std::cout << Contents of mycolors map << std::endl } // This is the type of your map. It matches the above definition exactly // Access the class s iterator type. It s part of the class, not the instance, so it s accessed via the scope operator // std::map has functions that return markers for the beginning and end of your container. // The actual order of the elements is unimportant - the point is that you can loop through all of them for (std::map< std::string, int >::iterator it = mycolors.begin(); it!= mycolors.end(); it++) { // Iterator contents are accessed via simple members or operators. Here, first is the key, and second is the value. std::cout << it->first << : << it->second << std::endl; } 37/83

5. Other Container Classes - The Standard Library has a number of other containers: - std::stack - std::queue - std::set - std::unordered_map - std::unordered_set - Their syntax is similar to that of std::map s 38/83

/83

1. Overview - In C++, you can cast an object of one type into an object of another - Within obvious limitations and reason - There are 4 ways to cast an object, each with its own function 40/83

2. static_cast<type> - Should be your first choice of cast - Implicit conversions (e.g. unsigned int to int) - If your compiler complains about implicit conversions, you re likely missing a static_cast - Can cast up inheritance hierarchies (derived to base) - Unnecessary in this case - Performs no runtime checks - So you need to know that the conversion is correct to avoid issues 41/83

// main.cpp int main(int argc, char *argv[]) { float f = 123.321f; int i = static_cast<int>(f); } std::cout << My float is << f <<. << std::endl; // My float is 123.321 std::cout << My int is << i <<. << std::endl; // My int is 123 // Truncates the float, doesn t round 42/83

3. dynamic_cast<type> - Used for handling polymorphism - Base to Derived - Also Derived to Base, like static_cast, but this is again implicit and unnecessary - You don t know what the type of the class is - dynamic_cast returns nullptr when the cast fails 43/83

// cartest.cpp bool isferrari(car *car) { if (dynamic_cast<ferrari*>(car)!= nullptr) { return true; } return false; } 44/83

4. reinterpret_cast<type> - Turns one type directly into another - No type safety - it just does it - Only general guarantee is that you get what you started with if you cast back to the original type - This is dangerous - You re overriding the intended use of an object - Real world use - Interfacing with opaque data types - Occurs sometimes when using external APIs 45/83

5. const_cast<type> - const_cast changes the const-ness of an object - e.g. a const int can become an int - This is dangerous - By changing const-ness, you may end up breaking an invariant elsewhere and putting the program into an indeterminate state - Avoid unless you specifically cannot - Should not need to 46/83

6. C-Style Casting - Objects may also be cast using the C syntax - TypeA *a = (TypeA*) ptrtotypeb; - This is dangerous because which of the 4 casts used is not immediately clear - C-style casts try a number of C++ casts, sometimes two successively, until one succeeds - This style of casting exists as a C legacy - Use C++ casts, not C-style casts (or we ll deduct). - int => float and float => int: Use static_cast! 47/83

6. C-Style Casting - C++ is also easier to search for - The casting operation is an explicit word rather than the type and some symbols - C++ style casts keep code more maintainable - Easier to remove or change casts - The intent of the programmer is clear - The number of possible errors is reduced 48/83

IV: RAII 1. RAII Defined 2. Consistency 3. Resource Ownership 4. Memory Management 2.0 5. Smart Pointers a. std::unique_ptr b. std::shared_ptr 6. The Rule of 3 and the Rule of 5 49/83

1. RAII Defined - Resource Acquisition Is Initialization - RAII is a C++ coding idiom that makes memory management logical, useful, less error-prone, and generally better - This is A Good Thing 50/83

1. RAII cont. - RAII means that the acquisition of a resource is done during its initialization - If Foo owns a Bar, it initializes its Bar in its constructor, and frees that Bar in its destructor - If Foo opens a file, it closes it - Foo must release the resources it acquires! - The class giveth, and the class taketh away 51/83

// unicorn.h class Unicorn { public: Unicorn(); void init(); private: Horn *m_horn; } // An init function? Uh-oh // unicorn.cpp Unicorn::Unicorn() {} // Initializer list? Hello? Where s the horn? (Don t answer that) void Unicorn::init() { // This is bad. // When a Unicorn is constructed, it is left in an unusable state. m_horn = new Horn(); // The caller must also call this init function. They might not, and that s bad. } 52/83

1. RAII cont. - A class must be ready to use after construction - Problems with the previous code: - Destructor: Even if we had one, how does it know if we ve init d or not? - Maybe add a boolean? But then how do we know if that s been init d? - Should we have a destroy() function? - What if we call init twice? Memory leak! 53/83

// unicorn.h class Unicorn { public: Unicorn(); ~Unicorn(); private: Horn *m_horn; } // Much better: A destructor. This looks useful // unicorn.cpp Unicorn::Unicorn() : m_horn(new Horn()) {} // Much better: Unicorn acquires a Horn during initialization Unicorn::~Unicorn() { } delete m_horn; // Much better: Unicorn frees the Horn during destruction // Unicorn owns this Horn resource 54/83

1. RAII cont. - RAII entails two very important concepts - Object consistency - Resource ownership - These force you to code well 55/83

2. Consistency - An object must be in a consistent state after construction - You should not have to call a special series of functions before the object is ready to use - Anti-patterns: - An init() function - A destroy() function 56/83

2. Consistency cont. - Clarifying note: An object must be in a consistent state and ready to use after construction - e.g. A list may be empty. Empty is ready to use - RAII doesn t necessarily mean every possible resource is acquired, but that objects are ready to use upon construction and clean up after themselves 57/83

3. Resource Ownership - Whoever initializes the resource owns it - Resources can be shared, but ultimately the object who creates it is responsible for it - Unless explicitly understood otherwise - e.g. Builder pattern - builder technically instantiates the object, but it s only used as a tool by the real owner of the object. 58/83

// builder.cpp CarBuilder carbuilder; carbuilder.setengine(...); carbuilder.setrims(...); carbuilder.settires(...); // This is a builder! Car *car = carbuilder.buildcar(); // Build the car! carbuilder technically creates the car, but the owner is the caller! delete car; // The caller deletes the car! 59/83

3. Resource Ownership - When ownership is clear, memory management duties are clear - Why? - Because RAII - I own it, I create it, I destroy it - If I give it to you, it must be clear if I am sharing or transferring ownership 60/83

4. Memory Management 2.0 - We know that objects should be in a consistent state after construction - We know that ownership of resources should be clearly defined - We know that automatic storage is preferred - We sit down to code - And we immediately realize we can t get all 3 at once - Good news: We realized wrong 61/83

4. Memory Management 2.0 - You can use pointer member variables to delay initialization - Sometimes you can t just call an object s constructor from the parent s initializer list - It is okay if a direct member variable is not possible - Don t violate consistency and make init and destroy functions - They will infect the rest of your code - But wait, this isn t automatic storage... 62/83

5. Smart Pointers - C++ includes as part of its Standard Library objects collectively referred to as smart pointers - Smart pointers are A Good Thing - unique_ptr - shared_ptr 63/83

5. Smart Pointers cont. - Smart pointers do two things - Manage memory - Clearly delineate resource ownership - Using raw new and delete is an antipattern! - (oh no!) - This is another common deduction 64/83

5. Smart Pointers cont. - Smart pointers are wrapper classes for raw pointers - They take care of deleting objects when they (the smart pointers) go out of scope - Smart pointers are to be allocated with automatic storage - This is the whole point - you can allocate dynamic memory but reap the benefits of automatic storage! 65/83

5.a. std::unique_ptr - As the name implies, unique_ptr denotes unique (sole) ownership - This is my horn - You may not have the horn - unique_ptrs cannot be copied - This is the one, true horn, of which graven images are strictly prohibited by the compiler - unique_ptr deletes its object in its destructor - I take my horn to the grave 66 /83

5.a. std::unique_ptr cont. - You can use.get() to get the raw pointer - If you really insist, you may touch the horn - A raw pointer indicates that you do not own the resource - The unique_ptr owns it - This can obviously be abused - Who put these sparkles on my horn? - But then it s your (the caller s) fault. This should be understood because you don t own it - You won t need to use.get() yourself in 123 but you might see it in the stencil code 67 /83

// unicorn.h class Unicorn { public: Unicorn(); private: } std::unique_ptr<horn> m_horn; // unicorn.cpp Unicorn::Unicorn() : m_horn(std::make_unique<horn>()) {} // We can initialize a unique_ptr with std::make_unique (or like a regular pointer) Unicorn::~Unicorn() { // Remember, unique_ptr has automatic storage, so its destructor is automatically called } // when Unicorn s destructor is called (that is, when unique_ptr goes out of scope). 68/83

// main.cpp #include Unicorn.h int main(int argc, char *argv[]) { std::unique_ptr<unicorn> uniqueunicorn = std::make_unique<unicorn>(); // Instantiate the unique_ptr uniqueunicorn = std::make_unique<unicorn>(); // Delete the old Unicorn and replace it with a new one uniqueunicorn->func(); // We can access the pointer via the overloaded -> operator // Yes, you can overload operators in C++ } // No need to call a destructor. uniqueunicorn has automatic storage, // and it cleans up that Unicorn object for us. 69/83

5.b. std::shared_ptr - As the name implies, shared_ptr denotes multiple ownership - Use when the lifetime of a resource could extend beyond any particular owner s need - Twilight Sparkle and Amethyst Star share a horn and we don t know who will find their own horn first. 70/83

5.b. std::shared_ptr cont. - Shared resources are not common - Central planners generally don t understand unicorns - They can make your code confusing and hard to reason about - Who gets the horn when and for how long? - According to Bjarne Stroustrup (C++ s creator), shared_ptr should only be used as a last resort - A true unicorn has a mighty horn - no horn-sharing needed! 71/83

// unicorn.h class Unicorn { public: Unicorn(); private: } std::shared_ptr<horn> m_horn; // unicorn.cpp Unicorn::Unicorn() : m_horn( std::make_shared<horn>() ) {} // We initialize a shared pointer with std::make_shared Unicorn::~Unicorn() { } 72/83

5.c. Sneak-peak - What if I want to change who owns something? - E.G. a std::unique_ptr - What if I want to change an object s location in memory but not *copy* it? - Stay tuned: More semantics in the Advanced C++ slides 73/83

6. The Rule of 5 - The Rule of 5: If you define any of - Destructor - Copy constructor - Copy assignment operator - Move constructor - Move assignment operator 74/83

6. The Rule of 5 - What happens conceptually when object is copied? - Do both objects point to the same resource? - Does the new object get a copy of the resource? - Can it even be copied? 75/83

6. The Rule of 5 - Example: std::string - Should both strings objects point to the same block of memory holding the chars? - Or should the new string have a copy of that memory? - The second choice is preferred 76/83

6. The Rule of 5 - Example: A VBO class - Should the new VBO class copy the VBO id of the first one? - Should it attempt to make a copy of the GPU memory and make a new vbo id? - Should it even be copyable? - Probably not 77/83

6. The Rule of 5 - But what does copying have to do with destructors? - std::string example: Assume the two copies pointed to the same block of memory. One is freed and has its destructor called. The other now has an invalid pointer! - VBO example: Same deal. If you just copied the VBO id, when one was deleted, it would invalidate the other 78/83

6. The Rule of 5 - If copying doesn t make sense or is undesirable, you can enforce that in your code - C++11 and beyond (what we use in 123): Define copy constructor and copy assignment operator as = delete. 79/83

// vbo.h class VBO { public: VBO(); // Rule of 3: VBO(const &VBO) = delete; // Copy constructor VBO& operator=(const &VBO) = delete; // Copy assignment operator ~VBO(); // Destructor private: } GLuint m_vboid; 80/83

// vbo.cpp VBO::VBO() : m_vboid(0) // We initialize the id safely to 0 { glgenbuffers(1, &m_vboid); // Do initialization that can t be done in the initializer list } // Copying here doesn t make sense // We probably don t even want the client to be able to move huge chunks of GPU memory around VBO::~VBO() { } gldeletebuffers(1,&m_vboid);// Free resources in the destructor 81/83