lesson5: C++11
生活随笔
收集整理的這篇文章主要介紹了
lesson5: C++11
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
統一的列表初始化
#include <iostream> #include <vector> #include <list> #include <map> #include <set> using namespace std;class Date { public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}private:int _year;int _month;int _day; };int main() {int x1 = 1;// 能看懂,但不建議使用int x2 = { 2 };int x3 { 2 };Date d1(2022, 11, 22);// ->調用構造函數// 能看懂,但不建議使用Date d2 = {2022, 11, 11}; // ->調用構造函數Date d3{ 2022, 11, 11 };// ->調用構造函數vector<int> v1 = { 1, 2, 3, 4, 5, 6 };vector<int> v2 { 1, 2, 3, 4, 5, 6 };list<int> lt1 = { 1, 2, 3, 4, 5, 6 };list<int> lt2{ 1, 2, 3, 4, 5, 6 };auto x = { 1, 2, 3, 4, 5, 6 };cout << typeid(x).name() << endl;return 0; }- ?C++11中增大了{ }的使用范圍,要求能看懂,但是不建議使用
?真正用途
#include <iostream> #include <vector> #include <list> #include <map> #include <set> using namespace std;class Date { public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}private:int _year;int _month;int _day; };int main() {Date d1(2022, 11, 22);Date d2 = {2022, 11, 11}; Date d3{ 2022, 11, 11 };vector<Date> v3 = {d1, d2, d3};// C++11中{}的真正用途vector<Date> v4 = { { 2022, 1, 1 }, {2022, 11, 11} };map<string, string> dict = { { "sort", "排序" }, { "insert", "插入" } };// 賦值重載// 這里的auto無法自動推導,所以自己顯示類型initializer_list<pair<const string, string>> kvil = { { "left", "左邊" }, { "left", "左邊" } };dict = kvil;return 0; }- vector<Date> v4 = { { 2022, 1, 1 }, {2022, 11, 11} };
initializer_list
?
?
- ??C++11增加了?initializer_list,以及對vector和list等容器的更新,所以才支持上面的用法
?總結:
- C++11以后一切對象都可以用列表初始化,但是建議普通對象還是用以前的方式初始化,
- 容器如果有需求的話就可以用列表初始化
?decltype(拿到變量的類型)
#include <iostream> using namespace std;int main() {int x = 10;// typeid拿到只是類型的字符串,不能用這個再去定義對象什么的//typeid(x).name() y = 20;decltype(x) y1 = 20.22;auto y2 = 20.22;cout << y1 << endl;cout << y2 << endl;return 0; }?
- ?typeid拿到只是類型的字符串,不能用這個再去定義對象什么的
- decltype不僅僅可以拿到變量的類型,還可以去定義對象
?C++11新增的容器
- ?unordered_map和unordered_set比較有用,array和forward_list的價值不大
?容器array
#include <iostream> #include <array> using namespace std;int main() {const size_t N = 100;int a1[N];// C語言數組越界檢查,越界讀基本檢查不出來,越界寫是抽查a1[N];//a1[N] = 1;//a1[N + 5] = 1;// 越界讀寫都可以被檢查出來array<int, 1> a2;a2[N];a2[N] = 1;a2[N + 5] = 1;return 0; }- array容器只要是越界就一定能被檢查出來
- C語言數組越界檢查,越界讀基本檢查不出來,越界寫是抽查
array實際使用情況:
- array用得很少,一方面大家用c數組用慣了
- 用array不如用vector + resize去替代c數組
左值引用& 和?右值引用&&
#include <iostream> #include <utility> using namespace std;int main() {// 左值: 能取地址int* p = new int(0);int b = 1;const int c = 2;// 對左值的左值引用int*& rp = p;int& rb = b;const int& rc = c;int& pvalue = *p;// 右值:不能取地址10;1.1 + 2.2;// 對右值的右值引用int&& rr1 = 10;double&& rr2 = 1.1 + 2.2;// 對右值的左值引用// double& r1 = 1.1 + 2.2;// errorconst double& r1 = 1.1 + 2.2;// 對左值的右值引用//int&& rr5 = b;// errorint&& rr5 = move(b);return 0; }- 左值: 能取地址
- 右值: 不能取地址,如:臨時對象,const的不能被修改的對象
- 如果對右值使用左值引用,需要加上const
- 如果對左值使用右值引用,需要是使用move函數
左值引用可以解決的問題?
?左值引用無法解決的問題?
- ?引用返回的前提是返回值出了作用域之后還在,
- 但是無法解決string中的to_string的返回值,以及有些函數返回值是二維數組的問題
?右值引用的實際用途: 移動構造 + 移動賦值
?沒有移動構造
- ?這里的string需要自己寫,才能看到結果
- 這里的g++編譯器優化的更厲害,這里雖然定義了ret
但是沒有使用,所以g++一次都沒有拷貝構造 - -fno-elide-constructors可以取消編譯器的優化
?加上移動構造
- ?函數參數的匹配原則是會優先匹配最合適自己的參數,
- to__string(-3456)中的-3456是一個右值,會優先匹配到移動構造,會發生右值引用返回
- 右值:1.內置類型右值-純右值 2. 自定義類型右值 - 將亡值
- 將亡值就是快要亡了的值,所以它的值就可以直接交換(swap)
- 將拷貝構造變成移動構造,會極大的提高效率
沒加移動賦值
- ?g++編譯器優化的很厲害
?加上移動賦值
- ?移動賦值和移動構造一樣,減少了拷貝,提高了效率
?
- ?C++11以后,幾乎所有的容器插入接口都提供了右值版本
- ?插入過程中,如果傳遞對象是右值對象,那么就會進行資源轉移減少拷貝
?引用折疊
#include <iostream> #include <utility> using namespace std;void Fun(int& x) { cout << "左值引用" << endl; } void Fun(const int& x) { cout << "const 左值引用" << endl; }void Fun(int&& x) { cout << "右值引用" << endl; } void Fun(const int&& x) { cout << "const 右值引用" << endl; }// 萬能引用:t既能引用左值,也能引用右值 // 引用折疊 template<typename T> void PerfectForward(T&& t) {Fun(t); }int main() {PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b); // const 左值PerfectForward(std::move(b)); // const 右值return 0; }- ?模板參數的右值引用叫萬能引用,T&& t中的t即能引用左值,也能引用右值
- 這里會觸發引用折疊,編譯器會把它識別成左值
完美轉發解決引用折疊問題
- 完美轉發:保持t引用對象屬性(是編譯器能分出左值和右值)?
- 它通過std::forward<模板>(參數)實現
新的類功能
?C++中默認成員函數
- 構造 析構 拷貝構造
- 賦值重載 取地址重載 const取地址重載
- 和C++11新增的移動構造,移動賦值
- 如果你沒有自己實現移動構造函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中的任 意一個。
- 那么編譯器會自動生成一個默認移動構造。默認生成的移動構造函數,
- 對于內置類 型成員會執行逐成員按字節拷貝,
- 自定義類型成員,則需要看這個成員是否實現移動構造,
- 如果實現了就調用移動構造,沒有實現就調用拷貝構造
- 移動賦值運算符重載同上
強制生成默認函數的關鍵字default: ?
?禁止生成默認函數的關鍵字delete:
?
?可變參數模板
#include <iostream> using namespace std;// 可變參數的函數模板 template <class ...Args> void ShowList(Args... args) {cout << sizeof...(args) << endl; }int main() {string str("hello");ShowList();ShowList(1);ShowList(1, 'A');ShowList(1, 'A', str);return 0; }- sizeof...(args)// 求模板參數的個數
?使用可變參數模板
// 解決只有一個參數的情況 void ShowList() {cout << endl; }// Args... args代表N個參數包(N >= 0) template <class T, class ...Args> void ShowList(const T& val, Args... args) {cout << "ShowList("<<val<<", " << sizeof...(args) << "參數包)" << endl;ShowList(args...); }int main() {string str("hello");ShowList(1, 'A', str);return 0; }- ?通過遞歸調用去使用模板參數
?容器的emplace_back和push_back比較
#include <list> #include <iostream> using namespace std; class Date { public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){cout << "Date(int year = 1, int month = 1, int day = 1)" << endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date(const Date& d)" << endl;}Date& operator=(const Date& d){cout << "Date& operator=(const Date& d))" << endl;return *this;}private:int _year;int _month;int _day; };int main() {list<Date> lt1;cout << "---------------------------------" << endl;lt1.push_back(Date(2022, 11, 16));cout << "---------------------------------" << endl;lt1.emplace_back(2022, 11, 16);cout << "---------------------------------" << endl;return 0; }- ?emplace_back和push_back對內置類型的處理是一樣的
- ?因為emplace_back可以直接傳參數,就只會發生構造,比push_back少一次的拷貝
lambda表達式
#include <vector> #include <string> #include <iostream> #include <algorithm> using namespace std; struct Goods {string _name;double _price; //價格int _evaluate; //評價Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){} };struct ComparePriceLess {bool operator()(const Goods& gl, const Goods& gr){return gl._price < gr._price;} };struct ComparePriceGreater {bool operator()(const Goods& gl, const Goods& gr){return gl._price > gr._price;} }; int main() {vector<Goods> v = { { "蘋果", 2.1, 5 }, \{ "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠蘿", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater()); }-
在c++11之前要通過不同的標準排序
-
就需要傳不同的仿函數才能解決問題
#include <vector> #include <string> #include <iostream> #include <algorithm> using namespace std; struct Goods {string _name;double _price; //價格int _evaluate; //評價Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){} };int main() {vector<Goods> v = { { "蘋果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠蘿", 1.5, 4 } };sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price < g2._price;});sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price > g2._price;});sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate < g2._evaluate;});sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate > g2._evaluate;}); }
- ?引入lambda解決問題
lambda表達式語法?
[捕捉列表](參數列表)mutable->返回值類型{函數體實現}
- mutable:默認情況下,lambda函數總是一個const函數,mutable可以取消其常量 性。使用該修飾符時, 參數列表不可省略 (即使參數為空)。
- 在lambda函數定義中,參數列表和返回值類型都是可選部分,而捕捉列表和函數體可以為 空。
- 因此C++11中最簡單的lambda函數為:[]{}; 該lambda函數不能做任何事情。
- 定義了lambda,還需要調用
?
- 不傳參數,默認捕捉的對象不能修改
捕獲列表說明
捕捉列表描述了上下文中那些數據可以被lambda使用,以及使用的方式傳值還是傳引用。
- [var]:表示值傳遞方式捕捉變量var
- [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
- [&var]:表示引用傳遞捕捉變量var
- [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
- [this]:表示值傳遞方式捕捉當前的this指針
- ?[&, a]表示除了a是值傳遞,其他的都是引用傳遞
- auto f2 = [=, a]() {};
- 捕捉列表不允許變量重復傳遞 ,否則就會導致編譯錯誤
- 在塊作用域以外的lambda函數捕捉列表必須為空。
- 不同的棧幀,不可捕捉,比如f4要去捕捉其他棧幀的變量?
- ?lambda表達式之間不能相互賦值
- ?但是允許使用一個lambda表達式拷貝構造一個新的副本
- ?但是可以將lambda表達式賦值給相同類型的函數指針
函數對象與lambda表達式
#include <iostream> using namespace std; class Rate { public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;}private:double _rate; };// lambda_uuid class lambda_xxxx {};int main() {// 函數對象double rate = 0.49;Rate r1(rate);r1(10000, 2);// 仿函數lambda_uuid// lambda -> lambda_uuidauto r2 = [=](double monty, int year)->double{return monty*rate*year; };r2(10000, 2);auto r3 = [=](double monty, int year)->double{return monty*rate*year; };r3(10000, 2);return 0; }- ?實際在底層編譯器對于lambda表達式的處理方式,完全就是按照函數對象的方式處理的,即:如果定義了一個lambda表達式,編譯器會自動生成一個類,在該類中重載了operator()。
function包裝器?: 一般用來包裝lambda表達式
- Ret: 被調用函數的 返回類型
- Args…:被調用 函數的形參
- function就是一種適配器,是?lambda的另一種引用
- C++中的function本質是一個類模板,也是一個包裝器。?
?案例:逆波蘭表達式求值
- ?這里用包裝器就很好的解決這個問題
?bind 綁定
#include <iostream> #include <functional> #include <map> using namespace std; int Div(int a, int b) {return a / b; }int Plus(int a, int b) {return a + b; }int Mul(int a, int b, double rate) {return a * b * rate; }class Sub { public:int sub(int a, int b){return a - b;} };using namespace placeholders;int main() {// 調整個數, 綁定死固定參數function<int(int, int)> funcPlus = Plus;//function<int(Sub, int, int)> funcSub = &Sub::sub;function<int(int, int)> funcSub = bind(&Sub::sub, Sub(), _1, _2);function<int(int, int)> funcMul = bind(Mul, _1, _2, 1.5);map<string, function<int(int, int)>> opFuncMap = {{ "+", Plus},{ "-", bind(&Sub::sub, Sub(), _1, _2)}};cout << funcPlus(1, 2) << endl;cout << funcSub(1, 2) << endl;cout << funcMul(2, 2) << endl;cout << opFuncMap["+"](1, 2) << endl;cout << opFuncMap["-"](1, 2) << endl;cout << "------------------------" << endl;int x = 2, y = 10;cout << Div(x, y) << endl;// 調整順序, _1表示第一個參數,_2表示第二個參數function<int(int, int)> bindFunc2 = bind(Div, _2, _1);cout << bindFunc2(x, y) << endl;return 0; } 可以將bind函數看作是一個通用的 函數適配器 ,它接受一個可調用對象,生成一個新的可調用對象來“適應”原對象的參數列表。
調用bind的一般形式:auto newCallable = bind(callable,arg_list);
- 其中,newCallable本身是一個可調用對象,
- arg_list是一個逗號分隔的參數列表,
- 對應給定的 callable的參數。當我們調用newCallable時,newCallable會調用callable,并傳給它arg_list中的參數。
- arg_list中的參數可能包含形如_n的名字,其中n是一個整數,這些參數是“占位符”,表示 newCallable的參數,它們占據了傳遞給newCallable的參數的“位置”。
- 數值n表示生成的可調用對象中參數的位置:
- _1為newCallable的第一個參數,_2為第二個參數,以此類推。
- 數值n表示生成的可調用對象中參數的位置:
總結
以上是生活随笔為你收集整理的lesson5: C++11的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 004.前端面试排雷之唱、跳、rap三步
- 下一篇: 疑惑!AI中台到底为什么火了?道翰天琼认