如下是我最近幾個星期學習c++11作的一些記錄,包括收集的一些信息,整理的相關概念和寫的一些測試代碼。具體相關代碼我寫了24個cpp文件,託管在來github上面cpp11,記錄一下。html
爲了描述一個lambda,你必須提供:
它的捕捉列表:即(除了形參以外)它可使用的變量列表(」[&]」 在上面的記錄比較例子中意味着「全部的局部變量都將按照引用的方式進行傳遞」)。若是不須要捕捉任何變量,則使用 [],[=]表示值傳遞。
(可選的)它的全部參數及其類型(例如: (int a, int b) )。
組織成一個塊的函數行爲(例如:{ return v[a].name < v[b].name; })。
(可選的)使用」返回值類型後置語法「來指明返回類型。但典型狀況下,咱們僅從return語句中去推斷返回類型,若是沒有返回任何值,則推斷爲void。ios
Lambda表達式與STL算法一塊兒使用;c++
經過「函數體」後面的‘()’傳入參數。git
int n = [] (int x, int y) { return x + y; }(5, 4); cout << n << endl;
Lambda表達式 | C++11 FAQ 中文版github
C++11 lambda表達式 - KingsLanding 正則表達式
爲動態申請的內存提供異常安全
將動態申請內存的全部權傳遞給某個函數(不能給複製,只能移動)編程
從某個函數返回動態申請內存的全部權
在容器中保存指針數組
在那些要不是爲了不不安全的異常問題(以及爲了保證指針所指向的對象都被正確地刪除釋放),咱們不可使用內建指針的狀況下,咱們能夠在容器中保存unique_ptr以代替內建指針promise
當 shared_ref_cnt 被減爲0時,自動釋放 ptr 指針所指向的對象。當 shared_ref_cnt 與 weak_ref_cnt 都變成0時,才釋放 ptr_manage 對象。
如此以來,只要有相關聯的 shared_ptr 存在,對象就存在。weak_ptr 不影響對象的生命週期。當用 weak_ptr 訪問對象時,對象有可能已被釋放了,要先 lock()。
weak_ptr能夠保存一個「弱引用」,指向一個已經用shared_ptr進行管理的對象。爲了訪問這個對象,一個weak_ptr能夠經過shared_ptr的構造函數或者是weak_ptr的成員函數lock()轉化爲一個shared_ptr。當最後一個指向這個對象的shared_ptr退出其生命週期而且這個對象被釋放以後,將沒法從指向這個對象的weak_ptr得到一個shared_ptr指針,shared_ptr的構造函數會拋出異常,而weak_ptr::lock也會返回一個空指針。
algorigthm頭文件中定義了幾個頗有用的方法;
相應代碼見: cpp11_21_for_cppFunctionProm.cpp
對於一些collection(vector,list,set,map)能夠對其中每一個元素執行後面的函數;
auto lambda_echo = [](int i ) { std::cout << i << std::endl; }; std::vector<int> col{20,24,37,42,23,45,37}; for_each(col,lambda_echo);
transform。該算法用於實行容器元素的變換操做。有以下兩個使用原型,一個將迭代器區間[first,last)中元素,執行一元函數對象op操做,交換後的結果放在[result,result+(last-first))區間中。另外一個將迭代器區間[first1,last1)的元素i,依次與[first2,first2+(last-first))的元素j,執行二元函數操做binary_op(i,j),交換結果放在[result,result+(last1-first1)
改變來原始的容器元素
zip函數將傳進來的兩個參數中相應位置上的元素組成一個pair數組。若是其中一個參數元素比較長,那麼多餘的參數會被刪掉。
表示判斷集合中是否有某個元素符合條件;
和map()相似,filter()也接收一個函數和一個序列。和map()不一樣的是,filter()把傳入的函數依次做用於每一個元素,而後根據返回值是True仍是False決定保留仍是丟棄該元素。
remove_if()並不會實際移除序列[start, end)中的元素; 若是在一個容器上應用remove_if(), 容器的長度並不會改變(remove_if()不可能僅經過迭代器改變容器的屬性), 全部的元素都還在容器裏面. 實際作法是, remove_if()將全部應該移除的元素都移動到了容器尾部並返回一個分界的迭代器. 移除的全部元素仍然能夠經過返回的迭代器訪問到. 爲了實際移除元素, 你必須對容器自行調用erase()以擦除須要移除的元素.
是否是服務端編程剛開始都得從寫業務開始? - 回答做者: itlr
簡單的程序詮釋C++ STL算法系列之十八:transform
duration 是chrono命名空間下面的一個模板類型,它有一些實例類型以下:
typedef duration<long long, nano> nanoseconds; //納秒 typedef duration<long long, micro> microseconds;//微秒 typedef duration<long long, milli> milliseconds;//毫秒 typedef duration<long long> seconds; typedef duration<int, ratio<60> > minutes; typedef duration<int, ratio<3600> > hours;
咱們使用的時候可使用它的實例化類型來建立對象
secons sec{128}
當要取得一個duration實例類型的變量的值的時候,使用count成員函數
sec.count()
當想要對duration進行單位類型轉換的時候,可使用duration_cast<duration_type>進行強制類型轉換;
chono::minutes min = duration_cast<chono::minutes>(sec)
有三種類型 steady_clock(穩定經常使用)
system_clock(直接讀取系統時間,可能被人手動改變)
high_resolution_clock(精度更高,單在vc庫裏面就是system_clock())
std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); std::cout << "Hello World\n"; std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now(); std::cout << "Printing took " << std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() << "us.\n";
能夠認爲tuple是一個未命名的結構體,該結構體包含了特定的tuple元素類型的數據成員。特別須要指出的是,tuple中元素是被緊密地存儲的(位於連續的內存區域),而不是鏈式結構。元素的類型能夠不同。tuple元組定義了一個有固定數目元素的容器,其中的每一個元素類型均可以不相同,這與其餘容器有着本質的區別.是對pair的泛化。
能夠顯式地聲明tuple的元素類型,也能夠經過make_tuple()來推斷出元素類型。另外,可使用get()來經過索引(和C++的數組索引同樣,從0而不是從1開始)來訪問tuple中的元素。
經過make_tuple()來推斷出元素類型,以此來構造一個tuple,也能夠顯式定義一個tuple,若是隻有兩個元素可使用make_part進行賦值;tuple是對pair的泛化;
經過索引來 讀寫 tuple中的元素
tie函數用在式子左邊,能夠將變量鏈接到一個給定的tuple上,能夠經過tie()函數的使用方便的對tuple進行「解包」操做。解包時,咱們若是隻想解某個位置的值時,能夠用std::ignore佔位符來表示不解某個位置的值。
tie函數用在式子右邊,它會建立一個元組的左值引用.
經過該函數能夠將多個tuple鏈接起來造成一個tuple
關於tuple的遍歷,元素個數的獲取,見個人代碼
cpp11_19_tuple.cpp
C++11提供了新頭文件 <thread>、<mutex>、<atomic>、<future>
等用於支持多線程。
async()函數是一個簡單任務的」啓動」(launcher)函數。
代碼見:cpp11_14_async.cpp
join()保證了在t1和t2完成後程序纔會終結。這裏」join」的意思是等待線程返回後再往下執行。
一般咱們須要在執行完一個任務後獲得返回的結果。對於那些簡單的對返回值沒有概念的,我建議使用std::future。另外一種方法是,咱們能夠給任務傳遞一個參數,從而這個任務能夠把結果存在這個參數中。
經過this_thrad::get_id()獲得線程id發現main函數的id是1,其它線程id依次遞增。
使用mutex能夠進行關鍵區的互斥訪問
見代碼:cpp11_20_thread_lambda.cpp
#include<thread> #include<iostream> using namespace std; void f(vector<double>&, double* res); // 將結果存在res中 struct F { vector<double>& v; double* res; F(vector<double>& vv, double* p) :v{vv}, res{p} { } void operator()(); //將結果存在res中 }; int main() { double res1; double res2; // f(some_vec,&res1) 在一個單獨的線程中執行 std::thread t1{std::bind(f,some_vec,&res1)}; // F(some_vec,&res2)() 在一個單獨的線程中執行 std::thread t2{F(some_vec,&res2)}; t1.join(); t2.join(); std::cout << res1 << " " << res2 << ‘\n’; }
lock(),調用線程將鎖住該互斥量。線程調用該函數會發生下面 3 種狀況:
若是該互斥量當前沒有被鎖住,則調用線程將該互斥量鎖住,直到調用 unlock以前,該線程一直擁有該鎖。
若是當前互斥量被其餘線程鎖住,則當前的調用線程被阻塞住。
若是當前互斥量被當前調用線程鎖住,則會產生死鎖(deadlock)。
unlock(), 解鎖,釋放對互斥量的全部權。
try_lock(),嘗試鎖住互斥量,若是互斥量被其餘線程佔有,則當前線程也不會被阻塞。線程調用該函數也會出現下面 3 種狀況,
若是當前互斥量沒有被其餘線程佔有,則該線程鎖住互斥量,直到該線程調用 unlock 釋放互斥量。
若是當前互斥量被其餘線程鎖住,則當前調用線程返回 false,而並不會被阻塞掉。
若是當前互斥量被當前調用線程鎖住,則會產生死鎖(deadlock)。
condition_variable與mutex的區別是: mutex 裏面包的是關鍵區,互斥訪問的,別人unlock後自動喚醒,lock與unlock是成對存在的。而condition_variable是線程之間同步的順序控制,一個線程wait另外一個線程運行完厚notify以後該線程才能繼續運行。
條件變量用於線程間的同步通訊。<condition_variable >
頭文件主要包含了與條件變量相關的類和函數。與條件變量相關的類包括 std::condition_variable和 std::condition_variable_any,還有枚舉類型std::cv_status另外還包括函數 std::notify_all_at_thread_exit()。
當 std::condition_variable 對象的某個 wait 函數被調用的時候,它使用 std::unique_lock(封裝 std::mutex) 來鎖住當前線程。當前線程會一直被阻塞,直到另一個線程在相同的 std::condition_variable 對象上調用了 notification 函數來喚醒當前線程。
這部分的代碼見 cpp11_22_condVir_proConsu.cpp
unique_lock 對象以獨佔全部權的方式( unique owership)管理 mutex 對象的上鎖和解鎖操做,所謂獨佔全部權,就是沒有其餘的 unique_lock 對象同時擁有某個 mutex 對象的全部權。unique_lock 對象只是簡化了 Mutex 對象的上鎖和解鎖操做,方便線程對互斥量上鎖,即在某個 unique_lock 對象的聲明週期內,它所管理的鎖對象會一直保持上鎖狀態;而 unique_lock 的生命週期結束以後,它所管理的鎖對象會被解鎖。
wait函數的一個原型:
void wait (unique_lock<mutex>& lck);
當前線程調用 wait() 後將被阻塞(此時當前線程應該得到了鎖(mutex),不妨設得到鎖 lck),直到另外某個線程調用 notify_* 喚醒了當前線程。
在線程被阻塞時,該函數會自動調用 lck.unlock() 釋放鎖,使得其餘被阻塞在鎖競爭上的線程得以繼續執行。另外,一旦當前線程得到通知(notified,一般是另外某個線程調用 notify_* 喚醒了當前線程),wait() 函數也是自動調用 lck.lock(),使得 lck 的狀態和 wait 函數被調用時相同)。
這句話的意思就是在線程被阻塞的時候,釋放mutex 的lock,在被喚醒的時候從新lock,直到這個lock出了做用域。
std::condition_variable::notify_one();
notify_one喚醒某個等待(wait)線程。若是當前沒有等待線程,則該函數什麼也不作,若是同時存在多個等待線程,則喚醒某個線程是不肯定的(unspecified)。
template <class Predicate> void wait (unique_lock<mutex>& lck, Predicate pred);
加條件的wait,表示如何這個條件不成立,就wait,成立就直接執行。至關於
if (!pred()) wait(lck);
看這個例子:
template <class Rep, class Period> cv_status wait_for (unique_lock<mutex>& lck, const chrono::duration<Rep,Period>& rel_time); template <class Rep, class Period, class Predicate> bool wait_for (unique_lock<mutex>& lck, const chrono::duration<Rep, Period>& rel_time, Predicate pred);
wait_for 能夠指定一個時間段,在當前線程收到通知或者指定的時間 rel_time 超時以前,該線程都會處於阻塞狀態。而一旦超時或者收到了其餘線程的通知,wait_for 返回,剩下的處理步驟和 wait() 相似。
能夠經過返回值std::cv_status的值來判斷是哪一種返回狀況;
std::condition_variable::notify_all();
notify_all 喚醒全部的等待(wait)線程。若是當前沒有等待線程,則該函數什麼也不作。
std::cv_status 枚舉類型取值
cv_status::no_timeout 表示 wait_for 或者 wait_until 沒有超時,即在規定的時間段內線程收到了通知。
cv_status::timeout 表示 wait_for 或者 wait_until 超時。
代碼在:cpp11_23_wait_for.cpp
【c++11FAQ】std::future和std::promise
C++11中引入的auto主要有兩種用途:自動類型推斷和返回值佔位。
聲明變量是不指明類型(但這時必須初始化)
當類型名比較長的時候(好比iterator,函數指針)
當變量的類型依賴於模板參數,須要編譯器來推斷;
用auto聲明的變量必須初始化
主要用在模板函數裏,用decltype來推斷類型;
注意在c++11標準裏須要在後面寫上實際的類型,或者推斷方式。而c++14則不須要
使用場景
template<typename U, typename V> auto foo(U u, V v) -> decltype(u*v){ return u*v; }
若是你僅僅是想根據初始化值爲一個變量推斷合適的數據類型,那麼使用auto是一個更加簡單的選擇。當你只有須要推斷某個表達式的數據類型,例如某個函數調用表達式的計算結果的數據類型,而不是某個變量的數據類型時,你才真正須要delctype。
【c++11 faq】decltype – 推斷表達式的數據類型
枚舉類(enum)(「強類型枚舉」)是強類型的,而且具備類域:
強類型是指不會隱式轉換爲int,有類域是指須要加"類名::"才能訪問它的枚舉值。
使用場景:
Color a6 = Color::blue; // 正確
enum 後面不加class仍是表示使用的傳統類型,對於c++11,c++98來講支持它轉換爲int,也支持它使用類名找到枚舉值(c++98不容許)
須要注意的是傳統enum不論是c++98仍是c++11都不能經過int賦值
能夠保證枚舉值所佔的字節大小,枚舉類的底層數據類型必須是有符號或無符號的整數類型(int/short/char等),默認狀況下是int。
enum class Color : char { red, blue }; // 緊湊型表示(一個字節)
【c++11 cpp11-snippets】enumeration.cpp
關於強制類型轉換的問題,不少書都討論過,寫的最詳細的是C++ 之父的《C++ 的設計和演化》。最好的解決方法就是不要使用C風格的強制類型轉換,而是使用標準C++的類型轉換符:static_cast, dynamic_cast。標準C++中有四個類型轉換符:static_cast、dynamic_cast、reinterpret_cast、和const_cast。下面對它們一一進行介紹。
【C++專題】static_cast, dynamic_cast, const_cast探討
在C++98標準裏,能夠將普通的重載函數從基類「晉級」到派生類裏來解決當基類與派生類的同名成員不在同一個做用域內時,派生類找不到父類同名成員的問題:
而在c++11中甚至連構造函數均可以這麼作
class A { public: A(int a):_a(a) { cout<<"A 的構造函數\n"; } void print(double a) { cout<<"print in A\n"; } int _a; }; class B : public A { public: using A::print; using A::A;//這句話使得B有了一個B(int)的默認構造函數;而默認構造函數只有一個; void print(int a,int b) { cout<<"print in B\n"; } }; int main() { B b(1); //B c;這句話編譯不經過,由於B沒有這樣的默認構造函數; b.print(1.1); return 0; }
正常狀況下字符串是位於R" "之間的,結束符就是"。但是字符串內部能夠包含"怎麼辦呢?好比R"fewga"rgare",改結束符,變成)",這樣就變成R"(fewga"rgare)",不會混淆了。但是問題又來了,若是字符串內包含)"呢?因而又引入了d-char-sequenceopt(能夠爲空),估且叫它分隔串吧,變成R"--(fewga)"rgare)--"這樣的形式,因而問題解決。因爲分隔串是用戶指定的,因此可使用不固定的結束符,不會混淆(除非有人太二)。字符串內有)-",我就用)#",有)#",我就用)**"。
」(…)」分隔法只不過是默認的分隔語法罷了。經過在「(…)」的(…)先後添加顯式的自定義分隔號(譯註:例以下面例子中的三個星號*),咱們還能夠創造出任何咱們想要的分隔語法。
// 字符串爲:"quoted string containing the usual terminator (")" R"***("quoted string containing the usual terminator (")")***"
ove(x) 意味着「你能夠把x當作一個右值」。
在C++11的標準庫中,全部的容器都提供了移動構造函數和移動賦值操做符,那些插入新元素的操做,如insert()和push_back(), 也都有了能夠接受右值引用的版本。最終的結果是,在沒有用戶干預的狀況下,標準容器和算法的性能都提高了,而這些都應歸功於拷貝操做的減小。
左值(賦值操做符「=」的左側,一般是一個變量)與右值(賦值操做符「=」的右側,一般是一個常數、表達式、函數調用)
自字義後綴用operator""定義,就是一種特殊的函數。後綴名必須如下劃線開頭,由於沒有下劃線的後綴是留給std用的。後綴的參數只能是unsigned long long、long double、const char或者const char + size_t。沒了,它就是這麼簡單易上手又很實用的特性。通常來講適合編爲後綴的是單位,如kg,km。
C++14預約義了一些標準的字面量,s用於建立std::string,如 "hello"s;h、min、s、ms、us、ns用於建立std::chrono::duration;i、il、if用於建立複數complex<double>、complex<long double>、complex<float>。
有如下四種數據標識的狀況,能夠被用戶定義後綴來使用用戶自定義數據標識:
整型標識:容許傳入一個unsigned long long或者const char*參數 浮點型標識:容許傳入一個long double或者const char*參數 字符串標識:容許傳入一組(const char*,size_t)參數 字符標識:容許傳入一個char參數。
根據 C++ 11 標準,只有下面這些簽名是合法的:
char const* unsigned long long long double char const*, std::size_t wchar_t const*, std::size_t char16_t const*, std::size_t char32_t const*, std::size_t
上面列出的第一個簽名不要同字符串相混淆,應該被稱爲原始字面量 raw literal 操做符。例如:
char const* operator"" _r(char const* s) { return s; } int main() { std::cout << 12_r << '\n'; }
#include <random> #include <iostream> int main() { std::default_random_engine generator; std::uniform_int_distribution<int> dis(0,100); for(int i=0;i<5;i++) { std::cout<<dis(generator)<<std::endl; } return 0; }
[[C++11]C++11帶來的隨機數生成器](http://www.cnblogs.com/egmkan...
能看懂別人的代碼就行,本身不須要用。由於這個如今看起來沒什麼用。
C++11中引入了序列for循環以實現區間遍歷的簡化。這裏所謂的區間能夠是任一種能用迭代器遍歷的區間,例如STL中由begin()和end()定義的序列。全部的標準容器,例如std::string、 初始化列表、數組,甚至是istream,只要定義了begin()和end()就行。
這裏是一個序列for循環語句的例子:
void f(const vector& v) { for (auto x : v) cout << x << ‘n’; for (auto& x : v) ++x; // 使用引用,方便咱們修改容器中的數據 }
C++11的基本思想是,容許非靜態(non-static)數據成員在其聲明處(在其所屬類內部)進行初始化。這樣,在運行時,須要初始值時構造函數可使用這個初始值。考慮下面的代碼:
class A { public: int a = 7; };