30
Talk Overview C++11 features Smart pointer family Rvalue references Thread Api

C++11 talk

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: C++11 talk

Talk Overview

● C++11 features● Smart pointer family● Rvalue references● Thread Api

Page 2: C++11 talk

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

Page 3: C++11 talk

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

Page 4: C++11 talk

unique_ptr example 1

void 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); // Don't 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'.

// Shouldn't access the fooPtr members after the move....

}

Page 5: C++11 talk

unique_ptr example 2

typedef 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.

}

Page 6: C++11 talk

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

Page 7: C++11 talk

shared_ptr example

typedef std::shared_ptr<Foo> FooSharedPtr;

void doSomething(FooSharedPtr fooPtr) {fooPtr->doSomething();

}

int main() {// Foo* foo(new Foo(1)); // Don't do this.// FooSharedPtr fooPtr1(foo); // The foo pointer is 'exclusively' shared-owned by // FooSharedPtr fooPtr2(foo); // the two shared pointers that don't 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.

Page 8: C++11 talk

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>();

Page 9: C++11 talk

shared_ptr gotcha

Beware 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.}

Page 10: C++11 talk

weak_ptr

● Observes the shared_ptr without affecting its lifetime

● Has to do null check on shared_ptr before using ittypedef 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.

}

Page 11: C++11 talk

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

Page 12: C++11 talk

II. Rvalue references

● Core language runtime feature in C++11● Tackles two problems

● Move semantics● Perfect forwarding

● Syntactic symbol &&

Page 13: C++11 talk

What is an Rvalue(Simple Definition)

● An rvalue can only occur on the right side of an assignment(C world)

● Some examplesint a = 42;int b = 43;

// a and b are both l-values:a = b; // okb = a; // oka = a * b; // ok

// a * b is an rvalue:int c = a * b; // ok, rvalue on right hand side of assignmenta * b = 42; // error, rvalue on left hand side of assignment

Page 14: C++11 talk

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 lvalueint* p = &i; // ok, i is an lvalueint& foo(); // return value has a memory location.Foo() = 42; // ok, foo() is an lvalueint* p1 = &foo(); // ok, foo() is an lvalue

// rvalues:int foobar(); // return value is a temporary.int j = 0;j = foobar(); // ok, foobar() is an rvalueint* p2 = &foobar(); // error, cannot take the address of an rvaluej = 42; // ok, 42 is an rvalue

Page 15: C++11 talk

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

Page 16: C++11 talk

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.

}

Page 17: C++11 talk

Example 1 cont...

class SomeT {public:... // Move constructor. SomeT(SomeT&& other)

: obj_(other.obj_) // Copy the pointer. {

other.obj_ = nullptr; // Make sure the other's 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.}

other.obj_ = null;

Page 18: C++11 talk

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.

}

Page 19: C++11 talk

Rvalue Gotchas

● An rvalue parameter inside a method/function is an lvalue

● Don't re-use an object that is movedclass Foo {

... Foo(Foo&& other)

// 1. must explicitly call std::move on bar otherwise invokes bar copy constructor.: bar(std::move(other.bar))

{ // 2. Shouldn't access other.bar here as it is moved. }

...private:

Bar bar; // Implements the move constructor and assignment operator}

Page 20: C++11 talk

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 referencestemplate<typename T>void f(T&& param);

// auto declarations:auto&& var = ... ;

Page 21: C++11 talk

Need for perfect forwarding

● Template factory methods that need to forward arguments ● 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)));

}

Page 22: C++11 talk

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

Page 23: C++11 talk

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)

Page 24: C++11 talk

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();

}

Page 25: C++11 talk

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.

Page 26: C++11 talk

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

Page 27: C++11 talk

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';}

Page 28: C++11 talk

Thread-safe initialization

● Singleton pattern support using call_oncevoid 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 initializationr_ptr->do_something();

}

● Thread-safe local static variable initializationclass MyClass;

MyClass& getInstance() {static MyClass instance; // Thread-safe in C++11return instance;

}

Page 29: C++11 talk

Thread Api Gotcha

● Arguments passed to std::thread, std::async, and std::call_once are 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 abovestd::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); // doesn't work as expected…t.join();

}

Page 30: C++11 talk

How You Work Around It

● Use Lambdas with by-reference captures

// closure copied, but it holds refs to argsstd::thread t([&]{ return f(arg1, arg2, arg3, ...); });

● Arguments wrapped via std::ref and std::cref

// Use ref wrappers to explicitly tell the intentauto fut = std::async( f, std::ref(arg1), arg2, std::cref(arg3), ...);