1 CSCI 104 C++11 Features Design Patterns Mark Redekopp
2 Plugging the leaks SMART POINTERS
3 C++11, 14, 17 • Most of what we have taught you in this class are language features that were part of C++ since the C++98 standard • New, helpful features have been added in C++11, 14, and now 17 standards – Beware: compilers are often a bit slow to implement the standards so check the documentation and compiler version – You often must turn on special compile flags to tell the compiler to look for C++11 features, etc. • For g++ you would need to add: -std=c++11 or -std=c++0x • Many of the features in the these revisions to C++ are originally part of 3 rd party libraries such as the Boost library
4 Pointers or Objects? Both! • In C++, the dereference class Thing operator (*) should appear { before… }; – A pointer to an object int main() – An actual object { Thing t1; • "Good" answer is Thing *ptr = &t1 – A Pointer to an object // Which is legal? • "Technically correct" answer… *t1; *ptr; – EITHER!!!! } • Due to operator overloading we can make an object behave as a pointer – Overload operator *, &, ->, ++, etc.
5 A "Dumb" Pointer Class • We can make a class template <typename T> class dumb_ptr operate like a pointer { private: T* p_; public: • Use template parameter as dumb_ptr(T* p) : p_(p) { } T& operator*() { return *p_; } the type of data the T* operator->() { return p_; } dumb_ptr& operator++() // pre-inc pointer will point to { ++p_; return *this; } }; • Keep an actual pointer as int main() private data { int data[10]; • Overload operators dumb_ptr<int> ptr(data); for(int i=0; i < 10; i++){ • This particular class doesn't cout << *ptr; ++ptr; } really do anything useful } – It just does what a normal pointer would do
6 A "Useful" Pointer Class • I can add automatic template <typename T> class unique_ptr { private: memory deallocation T* p_; public: so that when my local unique_ptr(T* p) : p_(p) { } ~unique_ptr() { delete p_; } "unique_ptr" goes T& operator*() { return *p_; } T* operator->() { return p_; } unique_ptr& operator++() // pre-inc out of scope, it will { ++p_; return *this; } }; automatically delete int main() what it is pointing at { unique_ptr<Obj> ptr(new Obj); // ... ptr->all_words() // Do I need to delete Obj? }
7 A "Useful" Pointer Class • What happens when template <typename T> class unique_ptr { private: I make a copy? T* p_; public: • Can we make it unique_ptr(T* p) : p_(p) { } ~unique_ptr() { delete p_; } T& operator*() { return *p_; } impossible for T* operator->() { return p_; } unique_ptr& operator++() // pre-inc anyone to make a { ++p_; return *this; } }; copy of an object? int main() { – Remember C++ unique_ptr<Obj> ptr(new Obj); unique_ptr<Obj> ptr2 = ptr; provides a default // ... ptr2->all_words(); "shallow" copy // Does anything bad happen here? } constructor and assignment operator
8 Hiding Functions • Can we make it impossible for template <typename T> class unique_ptr anyone to make a copy of an { private: object? T* p_; public: – Remember C++ provides a default unique_ptr(T* p) : p_(p) { } "shallow" copy constructor and ~unique_ptr() { delete p_; } assignment operator T& operator*() { return *p_; } T* operator->() { return p_; } • Yes!! unique_ptr& operator++() // pre-inc – Put the copy constructor and { ++p_; return *this; } private: operator= declaration in the unique_ptr(const UsefultPtr& n); private section…now the unique_ptr& operator=(const UsefultPtr& n); implementations that the compiler }; provides will be private (not accessible) int main() • { You can use this technique to hide unique_ptr<Obj> ptr(new Obj); "default constructors" or other unique_ptr<Obj> ptr2 = ptr; // Try to compile this? functions }
9 A "shared" Pointer Class • Could we write a pointer class where template <typename T> we can make copies that somehow class shared_ptr { public: "know" to only delete the underlying shared_ptr(T* p); object when the last copy of the smart ~shared_ptr(); T& operator*(); pointer dies? shared_ptr& operator++(); • Basic idea } shared_ptr<Obj> f1() – shared_ptr class will keep a count of { how many copies are alive shared_ptr<Obj> ptr(new Obj); cout << "In F1\n" << *ptr << endl; – shared_ptr destructor simply return ptr; decrements this count } • If count is 0, delete the object int main() { shared_ptr<Obj> p2 = f1(); cout << "Back in main\n" << *p2; cout << endl; return 0; }
10 A "shared" Pointer Class • Basic idea – shared_ptr class will keep a count of int main() { how many copies are alive shared_ptr<Obj> p1(new Obj); – Constructors/copies increment this doit(p1); return 0; count } – shared_ptr destructor simply void doit(shared_ptr<Obj> p2) decrements this count { • If count is 0, delete the object if(...){ shared_ptr<Obj> p3 = p2; shared_ptr p1 ControlObjPtr } } ControlObj RefCnt: 1 Actual Pointer Object
11 A "shared" Pointer Class • Basic idea – shared_ptr class will keep a count of int main() { how many copies are alive shared_ptr<Obj> p1(new Obj); – shared_ptr destructor simply doit(p1); return 0; decrements this count } • If count is 0, delete the object void doit(shared_ptr<Obj> p2) { if(...){ shared_ptr<Obj> p3 = p2; shared_ptr p1 ControlObjPtr } } ControlObj RefCnt: 2 shared_ptr p2 Actual Pointer Object ControlObjPtr
12 A "shared" Pointer Class • Basic idea – shared_ptr class will keep a count of int main() { how many copies are alive shared_ptr<Obj> p1(new Obj); – shared_ptr destructor simply doit(p1); return 0; decrements this count } • If count is 0, delete the object void doit(shared_ptr<Obj> p2) { if(...){ shared_ptr<Obj> p3 = p2; shared_ptr p1 ControlObjPtr } } ControlObj RefCnt: 3 shared_ptr p2 Actual Pointer Object ControlObjPtr shared_ptr p3 ControlObjPtr
13 A "shared" Pointer Class • Basic idea – shared_ptr class will keep a count of int main() { how many copies are alive shared_ptr<Obj> p1(new Obj); – shared_ptr destructor simply doit(p1); return 0; decrements this count } • If count is 0, delete the object void doit(shared_ptr<Obj> p2) { if(...){ shared_ptr<Obj> p3 = p2; shared_ptr p1 ControlObjPtr } // p3 dies } ControlObj RefCnt: 2 shared_ptr p2 Actual Pointer Object ControlObjPtr
14 A "shared" Pointer Class • Basic idea – shared_ptr class will keep a count of int main() { how many copies are alive shared_ptr<Obj> p1(new Obj); – shared_ptr destructor simply doit(p1); return 0; decrements this count } • If count is 0, delete the object void doit(shared_ptr<Obj> p2) { if(...){ shared_ptr<Obj> p3 = p2; shared_ptr p1 ControlObjPtr } // p3 dies } // p2 dies ControlObj RefCnt: 1 Actual Pointer Object
15 A "shared" Pointer Class • Basic idea – shared_ptr class will keep a count of int main() { how many copies are alive shared_ptr<Obj> p1(new Obj); – shared_ptr destructor simply doit(p1); return 0; decrements this count } // p1 dies • If count is 0, delete the object void doit(shared_ptr<Obj> p2) { if(...){ shared_ptr<Obj> p3 = p2; } // p3 dies } // p2 dies ControlObj RefCnt: 0 Actual Pointer Object
16 C++ shared_ptr • #include <memory> C++ std::shared_ptr / #include "obj.h" boost::shared_ptr using namespace std; – Boost is a best-in-class C++ library of shared_ptr<Obj> f1() code you can download and use with { all kinds of useful classes shared_ptr<Obj> ptr(new Obj); // ... • Can only be used to point at dynamically cout << "In F1\n" << *ptr << endl; allocated data (since it is going to call return ptr; delete on the pointer when the reference } count reaches 0) int main() • Compile in g++ using '-std=c++11' since { shared_ptr<Obj> p2 = f1(); this class is part of the new standard cout << "Back in main\n" << *p2; library version cout << endl; return 0; } $ g++ -std=c++11 shared_ptr1.cpp obj.cpp
17 C++ shared_ptr • #include <memory> Using shared_ptr's you can put #include <vector> pointers into container objects #include "obj.h" using namespace std; (vectors, maps, etc) and not have to worry about iterating through int main() { and deleting them vector<shared_ptr<Obj> > myvec; • When myvec goes out of scope, it shared_ptr<Obj> p1(new Obj); deallocates what it is storing myvec.push_back( p1 ); (shared_ptr's), but that causes the shared_ptr<Obj> p2(new Obj); shared_ptr destructor to myvec.push_back( p2 ); automatically delete the Objs return 0; // myvec goes out of scope... • Think about your project } homeworks …this might be (have been) nice $ g++ -std=c++11 shared_ptr1.cpp obj.cpp
Recommend
More recommend