C++11 talk


Published on

A high level presentation about the new features in C++11

Published in: Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

C++11 talk

  1. 1. Talk Overview● C++11 features ● Smart pointer family ● Rvalue references ● Thread Api
  2. 2. I. C++11 Smart Pointers● auto_ptr deprecated● New types ● unique_ptr ● shared_ptr / weak_ptr● Automatic garbage collection● Use RAII idiom● Exception safe● STL container safe● Work with dynamically created arrays● Pointer semantics
  3. 3. unique_ptr details● auto_ptr replacement● No copy semantics allowed● Strict single ownership usage● Uses move semantics to transfer ownership● Restricted usage in STL containers● Custom deleter support
  4. 4. unique_ptr example 1void doSomethingWithFoo(std::unique_ptr<Foo> fooPtr) { fooPtr->doSomething();} // fooPtr automatically destructed at the end of the scope.int main(int argc, char* argv[]) { // Foo* foo(new Foo()); // std::unique_ptr<Foo> fooPtr1(foo); // Dont do this. // std::unique_ptr<Foo> fooPtr2(foo); // A foo pointer must be owned by single unique_ptr. std::unique_ptr<Foo> fooPtr(new Foo()); // Use RAII always. … doSomethingRisky(); // Exception safe, foo cleanup on stack unwind. ... // doSomeThingWithFoo(fooPtr); // Compiler error, cannot use copy constructor. doSomethingWithFoo(std::move(fooPtr)); // Transfer ownership using explicit move. // Shouldnt access the fooPtr members after the move. ...}
  5. 5. unique_ptr example 2typedef std::unique_ptr<Foo> FooUniquePtr;void doSomethingWithReference( FooUniquePtr& fooPtr) { fooPtr->doSomething();}int main(int argc, char* argv[]) { FooUniquePtr fooPtr1(new Foo(1)); FooUniquePtr fooPtr2(new Foo(2)); doSomethingWithReference(fooPtr1); // Passing a reference not ownership. std::vector<Foo*> foos; foos.push_back(fooPtr1.get()); // still own, passing just a pointer not ownership. foos.push_back(fooPtr2.get()); // ditto fooPtr1.reset(new Foo(3)); // Foo(1) gets destructed. fooPtr1 = std::move(fooPtr2); // move foo(2) into fooPtr1, causes Foo(3) destr. std::vector< FooUniquePtr> fooPtrs; fooPtrs.push_back(std::move(fooPtr1)); // Transfer ownership to the container.}
  6. 6. shared_ptr details● Multi/shared ownership usage● Thread safe (similar to primitives)● Custom destructor support● Susceptible to cyclic references memory leak● Uses copy semantics● Uses an atomic reference counter internally
  7. 7. shared_ptr exampletypedef std::shared_ptr<Foo> FooSharedPtr;void doSomething(FooSharedPtr fooPtr) { fooPtr->doSomething();}int main() { // Foo* foo(new Foo(1)); // Dont do this. // FooSharedPtr fooPtr1(foo); // The foo pointer is exclusively shared-owned by // FooSharedPtr fooPtr2(foo); // the two shared pointers that dont know each other. FooSharedPtr fooPtr1(new Foo(1)); FooSharedPtr fooPtr2(fooPtr1); // foo1 pointer is shared owned by the two sharedPtrs. FooSharedPtr fooPtr3; fooPtr3 = fooPtr2; // foo1 pointer is shared owned by the 3 sharedPtrs. fooPtr2.reset(new Foo(2)); // foo1 is shared by fooPtr1, fooPtr3 only. doSomething(fooPtr3); // foo1 is Shared by fooPtr1, fooPtr2 and the param.} // Both foo1, foo2 are both destroyed at the program exit.
  8. 8. shared_ptr variants● Custom deleter variant ● Exception-safe way of managing OS resources, custom memory blocks etc ● e.g. int main() { FILE* fp = fopen("/tmp/temp.txt", "rw"); std::shared_ptr<File, std::function<void (File*)> filePtr(fp, [ ] (File* fp) { fclose(fp);}); fseek(filePtr.get(), 42, SEEK_SET); // Do something else with the file }● make_shared variant ● Single memory allocation ● Requires a default constructor for T ● No facility for custom deleter specification ● Sample syntax: std::shared_ptr<Foo> = std::make_shared<Foo>();
  9. 9. shared_ptr gotchaBeware of shared_ptr cyclic referencese.g. typedef std::shared_ptr<Chicken> ChickenPtr; typedef std::shared_ptr<Egg> EggPtr; struct Chicken { EggPtr source_;}; struct Egg { ChickenPtr source_;}; void memoryLeakDemo1() { { ChickenPtr chickenPtr(new Chicken()); EggPtr eggPtr(new Egg()); chickenPtr->source_ = eggPtr; eggPtr->source_ = chickenPtr; // The cycle completes } // Memory leak here: Both go the Chicken and egg go out of scope here // and they are not garbage collected as each have reference to the other. }
  10. 10. weak_ptr● Observes the shared_ptr without affecting its lifetime● Has to do null check on shared_ptr before using it typedef std::shared_ptr<Chicken> ChickenPtr; typedef std::shared_ptr<Egg> EggPtr; struct Chicken { std::weak_ptr<Egg> source_;}; struct Egg { std::weak_ptr<Chicken> source_;}; void memoryLeakDemo2() { { ChickenPtr chickenPtr(new Chicken()); EggPtr eggPtr(new Egg()); chickenPtr->source_ = eggPtr; eggPtr->source_ = chickenPtr; // No shared_ptr cycle here. } // The Chicken and egg are garbage collected here. }
  11. 11. When to use which● Use unique_ptr when ● by default ● the single ownership usage is clear ● implementing a pImpl idiom● Use shared_ptr when ● the object needs to be shared ● not sure of the ownership details ● need flexibility in using STL containers ● using a pre-C++11 compiler● Prefer make_shared variant whenever possible
  12. 12. II. Rvalue references● Core language runtime feature in C++11● Tackles two problems ● Move semantics ● Perfect forwarding● Syntactic symbol &&
  13. 13. What is an Rvalue(Simple Definition)● An rvalue can only occur on the right side of an assignment(C world)● Some examples int a = 42; int b = 43; // a and b are both l-values: a = b; // ok b = a; // ok a = a * b; // ok // a * b is an rvalue: int c = a * b; // ok, rvalue on right hand side of assignment a * b = 42; // error, rvalue on left hand side of assignment
  14. 14. What is an Rvalue(C++ Definition)● Rvalue: an expression that is not an lvalue● Lvalue: any expression that refers to a memory location● Some examples int i = 42; i = 43; // ok, i is an lvalue int* p = &i; // ok, i is an lvalue int& foo(); // return value has a memory location. Foo() = 42; // ok, foo() is an lvalue int* p1 = &foo(); // ok, foo() is an lvalue // rvalues: int foobar(); // return value is a temporary. int j = 0; j = foobar(); // ok, foobar() is an rvalue int* p2 = &foobar(); // error, cannot take the address of an rvalue j = 42; // ok, 42 is an rvalue
  15. 15. Motivation 1● Eliminate deep copies on temporaries● Optimize certain pass-by-value cases● Some container operation optimizations● Give more flexibility to the programmer● Solve all the above problems using move semantics
  16. 16. Move Semantics Example 1● C++98 template swap method template <typename T> void swap(T& a, T& b) { T temp(a); // Two copies of a. what if a is very expensive to build? a = b; // Two copies of b. b = temp; // Two copies of a again. }● C++11 template swap method uses std::move● Std::move – Converts an lvalue to rvalue template <typename T> void swap(T& a, T& b) { T temp(std::move(a)); // One copy of a if T has a move constructor. a = std::move(b); // One copy of b if T has a move assignment operator. b = std::move(temp); // One of copy of b. }
  17. 17. Example 1 cont...class SomeT {public:... // Move constructor. SomeT(SomeT&& other) : obj_(other.obj_) // Copy the pointer. { other.obj_ = null; other.obj_ = nullptr; // Make sure the others obj_ is set to NULL } // Move assignment operator. SomeT& operator=(SomeT&& other) { // steal the member fields. std::swap(obj_, other.obj_); }...private: SomeLargeObject* obj_; // Some large object that is expensive to copy.}
  18. 18. Example 1 more code...// A funtion that returns SomeT by value.SomeT methodThatReturnsByValue();int main(){ SomeT some_t; ... // Compiler will automatically invoke the move assignment operator // instead of the copy assignment since it is an rvalue. some_t = methodThatReturnsByValue(); std::vector<some_t> some_ts; some_ts.reserve(4); // Lets assume the vector has a capacity of 4. for (int k = 0; k < 100; ++k) // Should cause some internal vector re-sizing calls. some_ts.push_back(some_t()); // Efficient since the SomeT has move constructor.}
  19. 19. Rvalue Gotchas● An rvalue parameter inside a method/function is an lvalue● Dont re-use an object that is moved class Foo { ... Foo(Foo&& other) // 1. must explicitly call std::move on bar otherwise invokes bar copy constructor. : bar(std::move(other.bar)) { // 2. Shouldnt access other.bar here as it is moved. } ... private: Bar bar; // Implements the move constructor and assignment operator }
  20. 20. Perfect forwarding● Used to preserve the original argument type across function calls● Uses std::forward<T> to preserve the calling type● Works only on template functions and auto situations● e.g. // Binds both rvalue and lvalue references template<typename T> void f(T&& param); // auto declarations: auto&& var = ... ;
  21. 21. Need for perfect forwarding● Template factory methods that need to forwardarguments● e.g.Problem: template<typename T, typename Arg> std::shared_ptr<T> factory(Arg arg) { // Passed by value even if the arg is a ref and T has a constructor that takes reference return std::shared_ptr<T>(new T(arg)); }Solution: // Matches both rvalue and lvalue references. template<typename T> std::shared_ptr<T> factory(Arg&& arg) { // preserves the arg type to the T constructor. return std::shared_ptr<T>(new T(std::forward<Arg>(arg))); }
  22. 22. Rvalue refs: Do you need to care● Use smart pointers to avoid object copies● Compiler optimization of temporary copies● STL Container emplace methods● Compiler upgrade: More efficient code out-of- box
  23. 23. III. C++11 Thread Api● Thread-aware memory model● Mostly compatible with boost thread api● Support for atomics, mutexes and locks● std::async, futures, packaged_tasks and promises● Conditional variables● Thread-local storage● Thread-safe Initialization (objects w/static storage, std::call_once)
  24. 24. Thread class● RAII idiom to create an TOE● Works with any callable type● Beware of the destructor (must call either of join() or detach() prior)● Movable but not copyable● Example syntax: void printInt(int x) { std::count << “x = “ << x << std::endl; } int main() { … int x = 10; std::thread t(printInt, x); … // Beware of any exceptions here. t.join(); }
  25. 25. Async method● Higher level template method abstraction● Returns the result via a future object● Two different implementations with similar API int funcToRun(); int main() { std::future<int> f = std::async(funcToRun); … int result = f.get(); // get the result now. } // funcToRun is executed per a launch policy: // Default: Implementation chooses one // std::launch::async: Run asynchronously. // std::launch::deferred: Run synchronously if f.get() called.
  26. 26. packaged_task class● High level template class abstraction● Encapsulates a callable type● Has a method that returns a future● Can be launched on the same thread or a different one● Makes thread-pool implementations easier
  27. 27. packaged_task example#include <iostream>#include <future>#include <thread>int main(){ std::packaged_task<int()> task([](){return 7;}); // pass lambda std::future<int> result = task.get_future(); // get a future std::thread(std::move(task)).detach(); // launch on a thread std::cout << "Waiting..."; result.wait(); std::cout << "Done!nResult is " << result.get() << n;}
  28. 28. Thread-safe initialization● Singleton pattern support using call_once void init_resource(Resource* rptr) { rptr = new Resource(); } std::once_flag resource_flag; Resource* r_ptr; void foo() { std::call_once(resource_flag, init_resource, r_ptr); // thread-safe once initialization r_ptr->do_something(); }● Thread-safe local static variable initialization class MyClass; MyClass& getInstance() { static MyClass instance; // Thread-safe in C++11 return instance; }
  29. 29. Thread Api Gotcha● Arguments passed to std::thread, std::async, and std::call_onceare unconditionally copied to thread private area std::thread t(f, arg1, arg2, arg3, ...); // copy of f is called with copies of args. auto fut = std::async(f, arg1, arg2, arg3, ...); // same as above std::call_once(flag, f, arg1, arg2, arg3, ...); // ditto● What does this mean? void processData(const SensorData& d); // fn that takes Pass-by-reference int main() { SensorData *pd = new (SensorAddr) SensorData; std::thread t(processData, *pd); // doesnt work as expected … t.join(); }
  30. 30. How You Work Around It● Use Lambdas with by-reference captures // closure copied, but it holds refs to args std::thread t([&]{ return f(arg1, arg2, arg3, ...); });● Arguments wrapped via std::ref and std::cref // Use ref wrappers to explicitly tell the intent auto fut = std::async( f, std::ref(arg1), arg2, std::cref(arg3), ...);