chapter 2 Concept
Language feature
- auto
- Defer to define variable type. (Like var in c#/JS.)
- it will discard
const
,volatile
andreference
.
- lambda
- Anonymous function (object)
- Syntax
[=] () mutable throw() -> typeid
- constexpr
- Keyword (modifier) similiar to
'const'
- Initialized in compile time. (
'const'
in runtime) - Can modify both type and function. (
'const'
only modifies type)
- Keyword (modifier) similiar to
range base for loop
- Since C++11, make traditional for-loop more readable, such as all elements in a container.
// captures_lambda_expression.cpp // compile with: /EHsc #include "stdafx.h" #include <iostream> #include <iterator> #include <algorithm> #include <vector> // fibonacci in compile time by constexpr constexpr int fabonacci(int n) { return (n < 2) ? 0 : (fabonacci(n - 1) + fabonacci(n - 2)); } // fibonacci in compile time by meta-function template <int N> struct fab_meta { enum { val = fab_meta<N - 2>::val + fab_meta<N - 1>::val }; }; template <> struct fab_meta<0> { enum { val = 1 }; }; template <> struct fab_meta<1> { enum { val = 1 }; }; using namespace std; int main() { const int N = 10; vector<int> vec(2, 1); for (int i = 2; i < N; i++) { vec.push_back(vec[i - 2] + vec[i - 1]); } std::for_each(vec.begin(), vec.end(), [](int i) { cout << i << " "; }); cout << endl; vector<int> series; series = { fabonacci(0), fabonacci(1), fabonacci(3), fabonacci(4), fabonacci(5), }; for(int fn : vec) cout << fn << ","; cout << endl; series = { fab_meta<0>::val, fab_meta<1>::val, fab_meta<2>::val, fab_meta<3>::val, fab_meta<4>::val, fab_meta<5>::val }; for (auto fn : vec) cout << fn << ","; } // Output: // 1,1,2,3,5,8,13,21,34,55 // 1,1,2,3,5,8,13,21,34,55 // 1,1,2,3,5,8,13,21,34,55
Syntax compare (
C11/14
vsC#
vsJava Script
)std::vector<int> v = {0, 1, 2, 3, 4, 5}; for (int it = v.begin(); it != v.end(); ++it) // Safer than C { std::cout << i << ' '; } auto show = [](const int& i) { std::cout << i << ' '; }; // Elegant: lib-level std::for_each(v.begin(), v.end(), show) {}; for (const int& i : v) // Syntax sugar: lang-level { std::cout << i << ' '; }
int[] v = new int[] {0, 1, 2, 3, 4, 5}; foreach(int i in v) { System.Console.Write("{0};",i); }
var v = [0, 1, 2, 3, 4, 5]; v.forEach( function(i) { console.log(i); } v.map(function(i) { // accumulates all of the results into a collection console.log(i); return i; });
Move semantics and memory management
rvalue reference (&&)
- For implement "move semantics" and "perfect forwarding" (reduce copy times)
rule of 5
copy constructor
copy assignment operator
destructor
move constructor
move assignment operator
class resource { int x = 0; }; class foo { resource* p; public: foo() : p{ new resource{} } ... // move constructor foo(foo&& other) : p{ other.p } { other.p = nullptr; } // move assignment operator foo& operator=(foo&& other) { if (&other != this) { delete p; p = other.p; other.p = nullptr; } return *this; } };
std::move
std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object.std::forward
template<class T> void wrapper(T&& arg) { foo(std::forward<T>(arg)); // Forward a single argument. }
- If a call to wrapper() passes an rvalue std::string, then T is deduced to std::string (not std::string&, const std::string&, or std::string&&), and std::forward ensures that an rvalue reference is passed to foo.
- If a call to wrapper() passes a const lvalue std::string, then T is deduced to const std::string&, and std::forward ensures that a const lvalue reference is passed to foo.
- If a call to wrapper() passes a non-const lvalue std::string, then T is deduced to std::string&, and std::forward ensures that a non-const lvalue reference is passed to foo.
Smart Pointer
- Replace pointer by
template class XXX_ptr<>
to avoid memory leak std::unique_ptr
- delete resource after object destroy (leve scope)std::shared_ptr
- reference count (static data member), increase when operator=shared_ptr<int> a; // a is empty void func() { shared_ptr<int> b( new int( 10 ) ); a = b; *a = 100; ... }
std:weak_ptr
- could point to both, lazy lock
- Replace pointer by
Parallel Pattern library (PPL)
Overview:
- The PPL provides a imperative programming model that resembles the Standard Template Library (STL) and promotes scalability and ease-of-use for developing concurrent applications.
Follow chapter 0, and compile with
Precompiled Header
→Not Using Precompiled Headers
.template <class Function> __int64 time_call(Function&& f) { __int64 begin = GetTickCount(); f(); return GetTickCount() - begin; } int fibonacci(int n) { retunr (n < 2)? 1: fibonacci(n-1) + fibonacci(n-2); } int wmain() { __int64 elapsed; array<int, 4> a = { 24, 26, 41, 42 }; // big Fibonacci numbers vector<tuple<int,int>> results1; // result of serial computation concurrent_vector<tuple<int,int>> results2; // result of parallel computation // Use the for_each algorithm to compute the results serially. elapsed = time_call([&] { for_each (begin(a), end(a), [&](int n) { results1.push_back(make_tuple(n, fibonacci(n))); }); }); wcout << L"serial time: " << elapsed << L" ms" << endl; // Use the parallel_for_each algorithm to perform the same task. elapsed = time_call([&] { parallel_for_each (begin(a), end(a), [&](int n) { results2.push_back(make_tuple(n, fibonacci(n))); }); sort(begin(results2), end(results2)); }); wcout << L"parallel time: " << elapsed << L" ms" << endl << endl; for_each (begin(results2), end(results2), [](tuple<int,int>& pair) { wcout << L"fib(" << get<0>(pair) << L"): " << get<1>(pair) << endl; }); }
- Fork Way // Ugly and more overloading
long fib_par(long n) { long result; if (n < 2) { result = n; } else { long f0, f1; fork2([&] { // lambda: call by all reference f1 = fib_par(n-1); }, [&] { // lambda: call by all reference f0 = fib_par(n-2); }); result = f0 + f1; } return result; }
std::thread
- The class thread represents a single thread of execution.
std::mutex
- The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads. (critical section)
std::future
- A mechanism to access the result of asynchronous operations.
std::promise
- The class template std::promise provides a facility to store a value or an exception that is later acquired asynchronously.
- Each promise is associated with a shared state, which contains some state information and a result which may be not yet evaluated
// thread, mutex
#include <iostream>
#include <map>
#include <string>
#include <chrono>
#include <thread>
#include <mutex>
std::map<std::string, std::string> g_pages;
std::mutex g_pages_mutex;
void save_page(const std::string &url)
{
// simulate a long page fetch
std::this_thread::sleep_for(std::chrono::seconds(2));
std::string result = "fake content";
std::lock_guard<std::mutex> guard(g_pages_mutex);
g_pages[url] = result;
}
int main()
{
std::thread t1(save_page, "http://foo");
std::thread t2(save_page, "http://bar");
t1.join();
t2.join();
// safe to access g_pages without lock now, as the threads are joined
for (const auto &pair : g_pages) {
std::cout << pair.first << " => " << pair.second << '\n';
}
}
Output:
http://bar => fake content
http://foo => fake content
// future, promise
#include <iostream>
#include <future>
#include <thread>
int main()
{
// future from a packaged_task
std::packaged_task<int()> task([](){ return 7; }); // wrap the function
std::future<int> f1 = task.get_future(); // get a future
std::thread(std::move(task)).detach(); // launch on a thread
// future from an async()
std::future<int> f2 = std::async(std::launch::async, [](){ return 8; });
// future from a promise
std::promise<int> p;
std::future<int> f3 = p.get_future();
std::thread( [&p]{ p.set_value_at_thread_exit(9); }).detach();
std::cout << "Waiting..." << std::flush;
f1.wait();
f2.wait();
f3.wait();
std::cout << "Done!\nResults are: "
<< f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n';
}
Output:
Waiting...Done!
Results are: 7 8 9