Talk Overview
● C++11 features● Smart pointer family● Rvalue references● Thread Api
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
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
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....
}
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.
}
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
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.
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>();
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.}
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.
}
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
II. Rvalue references
● Core language runtime feature in C++11● Tackles two problems
● Move semantics● Perfect forwarding
● Syntactic symbol &&
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
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
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
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.
}
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;
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.
}
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}
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 = ... ;
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)));
}
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
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)
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();
}
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.
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
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';}
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;
}
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();
}
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), ...);