C++11介紹

目錄

一、C++11的兩個小點

1.1、decltype

1.2、nullptr

二、列表初始化

2.1、C++98傳統的{}

2.2、C++11中的{}

2.3、C++11中的std::initializer_list

三、右值引用和移動語義

3.1、左值和右值

3.2、左值引用和右值引用

3.3、引用延長生命周期

3.4、左值和右值的參數匹配

3.5、右值引用和移動語義的使用場景

3.5.1、左值引用主要使用場景回顧

3.5.2、移動構造和移動賦值

3.5.3、右值引?和移動語義解決傳值返回問題

3.5.4、右值引?和移動語義在傳參中的提效

3.6類型分類

3.7、折疊引用

3.8、完美轉發

四、可變參數模版

4.1、基本語法及原理

4.2、包擴展

4.3、emplace系列接口

五、新的類功能

5.1、默認的移動構造和移動賦值

5.2、defult和delete

六、lambda

6.1、lambda表達式語法

6.2、捕捉列表

6.3、lambda的應?

6.4、lambda的原理

七、包裝器

7.1、function

7.2、bind


一、C++11的兩個小點

1.1、decltype

關鍵字decltype將變量的類型聲明為表達式指定的類型。如圖:

1.2、nullptr

由于C++中NULL被定義為字面量0,這樣可能會帶來一些問題,因為0既能表示指針常量,又能表示整型常量,所以出于清晰和安全的角度考慮,C++11中新增了nullptr,用于表示空指針。

二、列表初始化

2.1、C++98傳統的{}

C++98中?般數組和結構體可以?{}進?初始化。如圖:

2.2、C++11中的{}

  • C++11以后想統?初始化?式,試圖實現?切對象皆可?{}初始化,{}初始化也叫做列表初始化。
  • 內置類型?持,?定義類型也?持,?定義類型本質是類型轉換,中間會產?臨時對象,最后優化了以后變成直接構造。
  • {}初始化的過程中,可以省略掉=。
  • C++11列表初始化的本意是想實現?個?統?的初始化?式,其次他在有些場景下帶來的不少便利,如容器push/inset多參數構造的對象時,{}初始化會很?便。

示例代碼:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date(const Date& d)" << endl;}
private:int _year;int _month;int _day;
};int main()
{// C++98?持的 int a1[] = { 1, 2, 3, 4, 5 };int a2[5] = { 0 };Point p = { 1, 2 };// C++11支持的 // 內置類型支持 int x1 = { 2 };// ?定義類型支持 // 這?本質是?{ 2025, 1, 1}構造?個Date臨時對象 // 臨時對象再去拷?構造d1,編譯器優化后合?為?變成{ 2025, 1, 1}直接構造初始化// 運??下,我們可以驗證上?的理論,發現是沒調?拷?構造的 Date d1 = { 2025, 1, 1 };// 這?d2引?的是{ 2024, 7, 25 }構造的臨時對象,臨時對象有常性const Date& d2 = { 2024, 7, 25 };// 需要注意的是C++98?持單參數時類型轉換,也可以不?{} Date d3 = { 2025 };Date d4 = 2025;// 可以省略掉= Point p1{ 1, 2 };int x2{ 2 };Date d6{ 2024, 7, 25 };const Date& d7{ 2024, 7, 25 };// 下面語句不支持,只有{}初始化,才能省略= // Date d8 2025;vector<Date> v;v.push_back(d1);v.push_back(Date(2025, 1, 1));// ?起有名對象和匿名對象傳參,這?{}更有性價? v.push_back({ 2025, 1, 1 });return 0;
}

2.3、C++11中的std::initializer_list

  • 上?的初始化已經很?便,但是對象容器初始化還是不太?便,?如?個vector對象,我想?N個值去構造初始化,那么我們得實現很多個構造函數才能?持。而initializer_list可以讓一個構造函數接受任意個數的參數,如:vector<int>?v1 = {1,2,3}; vector<int> v2 = {1,2,3,4,5};
  • C++11庫中提出了?個std::initializer_list的類,auto il = { 10, 20, 30 };// the type of il is an initializer_list,這個類的本質是底層開?個數組,將數據拷?到數組中,std::initializer_list內部不直接存儲數組的數據,而是有兩個指針分別指向數組的開始和結束。
  • 這是它的?檔:initializer_list - C++ Reference,std::initializer_list?持迭代器遍歷。
  • 容器?持?個std::initializer_list的構造函數,也就?持任意多個值構成的{x1,x2,x3...} 進?初始化。STL中的容器?持任意多個值構成的{x1,x2,x3...}進?初始化,就是通過 std::initializer_list的構造函數?持的。

如圖:(這里只截取了vector和list)

vector:

list:

示例代碼:

三、右值引用和移動語義

C++98的C++語法中就有引?的語法,?C++11中新增了的右值引?語法特性,C++11之后我們之前學習的引?就叫做左值引?。?論左值引?還是右值引?,都是給對象取別名。

3.1、左值和右值

  • 左值是?個表?數據的表達式(如變量名或解引?的指針),?般是有持久狀態,存儲在內存中,我們可以獲取它的地址,左值可以出現賦值符號的左邊,也可以出現在賦值符號右邊。定義時const 修飾符后的左值,不能給他賦值,但是可以取它的地址。
  • 右值也是?個表?數據的表達式,要么是字?值常量、要么是表達式求值過程中創建的臨時對象 等,右值可以出現在賦值符號的右邊,但是不能出現出現在賦值符號的左邊,右值不能取地址。
  • 值得?提的是,左值的英?簡寫為lvalue,右值的英?簡寫為rvalue。傳統認為它們分別是left?value、right value 的縮寫。現代C++中,lvalue 被解釋為loactor value的縮寫,可意為存儲在內存中、有明確存儲地址可以取地址的對象,? rvalue 被解釋為 read value,指的是那些可以提供數據值,但是不可以尋址,例如:臨時變量,字?量常量,存儲于寄存器中的變量等,也就是說左值和右值的核?區別就是能否取地址

注意:引用了右值的變量的屬性是左值。例如:int&& r1 = 10;10是右值,但是r1是左值。

示例代碼:

int main()
{// 左值:可以取地址 // 以下的p、b、c、*p、s、s[0]就是常?的左值 int* p = new int(0);int b = 1;const int c = b;*p = 10;string s("111111");s[0] = 'x';cout << &c << endl;cout << (void*)&s[0] << endl;// 右值:不能取地址 double x = 1.1, y = 2.2;// 以下?個10、x + y、fmin(x, y)、string("11111")都是常?的右值 10; x + y;fmin(x, y);string("11111");//右值取地址會報錯//cout << &10 << endl;//cout << &(x+y) << endl;//cout << &(fmin(x, y)) << endl;//cout << &string("11111") << endl;return 0;
}

3.2、左值引用和右值引用

  • Type& r1 = x;? Type&& rr1 = y;第?個語句就是左值引?,左值引?就是給左值取別名,第?個就是右值引?,同樣的道理,右值引?就是給右值取別名。
  • 左值引?不能直接引?右值,但是const左值引?可以引?右值。
  • 右值引?不能直接引?左值,但是右值引?可以引?move(左值)。
  • template <class T>?typename remove_reference<T>::type&& move (T&& arg);
  • move是庫??的?個函數模板,本質內部是進?強制類型轉換,當然他還涉及?些引?折疊的知識,這個下面會細講。
  • 需要注意的是變量表達式都是左值屬性,也就意味著?個右值被右值引?綁定后,右值引?變量的變量表達式的屬性是左值
  • 語法層?看,左值引?和右值引?都是取別名,不開空間。從匯編底層的?度看下?代碼中r1和rr1 匯編層實現,底層都是?指針實現的,沒什么區別。底層匯編等實現和上層語法表達的意義有時是背離的,所以不要?起去理解,互相佐證,這樣反?是陷?迷途。

move聲明:

示例代碼:

int main()
{// 左值:可以取地址 // 以下的p、b、c、*p、s、s[0]就是常?的左值 int* p = new int(0);int b = 1;const int c = b;*p = 10;string s("111111");s[0] = 'x';double x = 1.1, y = 2.2;// 左值引?給左值取別名 int& r1 = b;int*& r2 = p;int& r3 = *p;string& r4 = s;char& r5 = s[0];// 右值引?給右值取別名 int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);string&& rr4 = string("11111");// 左值引?不能直接引?右值,但是const左值引?可以引?右值 const int& rx1 = 10;const double& rx2 = x + y;const double& rx3 = fmin(x, y);const string& rx4 = string("11111");// 右值引?不能直接引?左值,但是右值引?可以引?move(左值) int&& rrx1 = move(b);int*&& rrx2 = move(p);int&& rrx3 = move(*p);string&& rrx4 = move(s);//底層(匯編層面)并不分左值引用和右值引用,在底層它們都是一樣的//語法上通不過是因為上層封裝時將他們封裝為不同類型//編譯器檢測語法時,因為類型不同報錯//所以只要強轉成相同類型就可以了,move的本質也是在強轉string&& rrx5 = (string&&)s;// b、r1、rr1都是變量表達式,都是左值 cout << &b << endl;cout << &r1 << endl;cout << &rr1 << endl;// 這?要注意的是,rr1的屬性是左值,所以不能再被右值引?綁定,除非move?下 int& r6 = r1;// int&& rrx6 = rr1;int&& rrx6 = move(rr1);return 0;
}

注意:右值底層其實是有地址的,但是在語法層取不到。

3.3、引用延長生命周期

右值引?可?于為臨時對象延??命周期,const 的左值引?也能延?臨時對象?存期,但這些對象?法被修改。

上圖中,r3引用的是右值,但是r3本身的屬性是左值,所以可以延長臨時對象的生命周期。

3.4、左值和右值的參數匹配

  • C++98中,我們實現?個const左值引?作為參數的函數,那么實參傳遞左值和右值都可以匹配。
  • C++11以后,分別重載左值引?、const左值引?、右值引?作為形參的 f 函數,那么實參是左值會匹配 f (左值引?),實參是const左值會匹配 f (const 左值引?),實參是右值會匹配f(右值引?)。
  • 右值引?變量在?于表達式時屬性是左值,這個設計這?會感覺跟怪,后面我們講右值引?的使?場景時,就能體會這樣設計的價值了。

示例代碼:

void f(int& x)
{std::cout << "左值引用重載 f(" << x << ")\n";
}void f(const int& x)
{std::cout << "const 的左值引用重載 f(" << x << ")\n";
}void f(int&& x)
{std::cout << "右值引用重載 f(" << x << ")\n";
}int main()
{int i = 1;const int ci = 2;f(i); // 調用 f(int&) f(ci); // 調用 f(const int&) f(3); // 調用 f(int&&),如果沒有 f(int&&) 重載則會調用 f(const int&) f(std::move(i)); // 調用 f(int&&) // 右值引用變量在用于表達式時是左值 int&& x = 1;f(x); // 調用 f(int& x) f(std::move(x)); // 調用 f(int&& x) return 0;
}

效果:

3.5、右值引用和移動語義的使用場景

3.5.1、左值引用主要使用場景回顧

左值引?主要使?場景是在函數中左值引?傳參和左值引?傳返回值時減少拷?,同時還可以修改實參和修改返回對象的價值。左值引?已經解決?多數場景的拷?效率問題,但是有些場景不能使?傳左值引?返回,如下面addStrings和generate函數,C++98中的解決?案只能是被迫使?輸出型參數解決。那么C++11以后這?可以使?右值引?做返回值解決嗎?顯然是不可能的,因為這?的本質是返回對象是?個局部對象,函數結束這個對象就析構銷毀了,右值引?返回也?法改變對象已經析構銷毀的事實。

代碼:

class Solution 
{
public:// 傳值返回需要拷貝string addStrings(string num1, string num2) {string str;int end1 = num1.size() - 1, end2 = num2.size() - 1;// 進位 int next = 0;while (end1 >= 0 || end2 >= 0){int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;int ret = val1 + val2 + next;next = ret / 10;ret = ret % 10;str += ('0' + ret);}if (next == 1)str += '1';reverse(str.begin(), str.end());return str;}
};class Solution 
{
public:// 這里的傳值返回拷?代價就太大了 vector<vector<int>> generate(int numRows) {vector<vector<int>> vv(numRows);for (int i = 0; i < numRows; ++i){vv[i].resize(i + 1, 1);}for (int i = 2; i < numRows; ++i){for (int j = 1; j < i; ++j){vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];}}return vv;}
};

3.5.2、移動構造和移動賦值

  • 移動構造函數是?種構造函數,類似拷?構造函數,移動構造函數要求第?個參數是該類類型的引?,但是不同的是要求這個參數是右值引?,如果還有其他參數,額外的參數必須有缺省值。
  • 移動賦值是?個賦值運算符的重載,他跟拷?賦值構成函數重載,類似拷?賦值函數,移動賦值函數要求第?個參數是該類類型的引?,但是不同的是要求這個參數是右值引?。
  • 對于像string/vector這樣的深拷?的類或者包含深拷?的成員變量的類,移動構造和移動賦值才有意義,因為移動構造和移動賦值的第?個參數都是右值引?的類型,他的本質是要“竊取”引?的右值對象的資源,?不是像拷?構造和拷?賦值那樣去拷?資源,從提?效率。下?的bit::string 樣例實現了移動構造和移動賦值,我們需要結合場景理解。

示例代碼:

namespace bit
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){cout << "string(char* str)-構造" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}//拷貝構造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 拷?構造" << endl;reserve(s._capacity);for (auto ch : s){push_back(ch);}}// 移動構造 //傳入的是臨時創建的對象,即將亡值,不能取地址,用完就會消亡//所以直接交換資源string(string&& s){cout << "string(string&& s) -- 移動構造" << endl;swap(s);}string& operator=(const string& s){cout << "string& operator=(const string& s) -- 拷?賦值" <<endl;if (this != &s){_str[0] = '\0';_size = 0;reserve(s._capacity);for (auto ch : s){push_back(ch);}}return *this;}// 移動賦值 string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移動賦值" << endl;swap(s);return *this;}~string(){cout << "~string() -- 析構" << endl;delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];if (_str){strcpy(tmp, _str);delete[] _str;}_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity *2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}size_t size() const{return _size;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;};
}

3.5.3、右值引?和移動語義解決傳值返回問題

注意:下面所有測試都基于上面實現的string類。

右值對象構造,只有拷?構造,沒有移動構造的場景:

圖1展?了vs2019 debug環境下編譯器對拷?的優化,左邊為不優化的情況下,兩次拷?構造,右 邊為編譯器優化的場景下連續步驟中的拷?合?為?變為?次拷?構造。

需要注意的是在vs2019的release和vs2022的debug和release,下?代碼優化為?常恐怖,會直接 將str對象的構造,str拷?構造臨時對象,臨時對象拷?構造ret對象,合三為?,變為直接構造。 變為直接構造。要理解這個優化要結合局部對象?命周期和棧幀的?度理解,如圖3所?。

linux下可以將下?代碼拷?到test.cpp?件,編譯時? g++ test.cpp -fno-elide-constructors 的?式關閉構造優化,運?結果可以看到圖1左邊沒有優化的兩次拷?。

圖一:

右值對象構造,有拷?構造,也有移動構造的場景:

圖2展?了vs2019 debug環境下編譯器對拷?的優化,左邊為不優化的情況下,兩次移動構造,右 邊為編譯器優化的場景下連續步驟中的拷?合?為?變為?次移動構造。

需要注意的是在vs2019的release和vs2022的debug和release,下?代碼優化為?常恐怖,會直接 將str對象的構造,str拷?構造臨時對象,臨時對象拷?構造ret對象,合三為?,變為直接構造。 要理解這個優化要結合局部對象?命周期和棧幀的?度理解,如圖3所?。

linux下可以將下?代碼拷?到test.cpp?件,編譯時? g++ test.cpp -fno-elide-constructors 的?式關閉構造優化,運?結果可以看到圖1左邊沒有優化的兩次移動。

圖二:

圖三:

右值對象賦值,只有拷?構造和拷?賦值,沒有移動構造和移動賦值的場景:

圖4左邊展?了vs2019 debug和 g++ test.cpp -fno-elide-constructors 關閉優化環境下編譯器的處理,?次拷?構造,?次拷?賦值。

需要注意的是在vs2019的release和vs2022的debug和release,下?代碼會進?步優化,直接構造 要返回的臨時對象,str本質是臨時對象的引?,底層?度?指針實現。運?結果的?度,我們可以 看到str的析構是在賦值以后,說明str就是臨時對象的別名。

圖四:

右值對象賦值,既有拷?構造和拷?賦值,也有移動構造和移動賦值的場景:

圖5左邊展?了vs2019 debug和 g++ test.cpp -fno-elide-constructors 關閉優化環境下編譯器的處理,?次移動構造,?次移動賦值。

需要注意的是在vs2019的release和vs2022的debug和release,下?代碼會進?步優化,直接構造 要返回的臨時對象,str本質是臨時對象的引?,底層?度?指針實現。運?結果的?度,我們可以 看到str的析構是在賦值以后,說明str就是臨時對象的別名。

圖五:

總結:如果傳入的是右值,對于自定義類型而言,右值又叫將亡值,使用之后就會消亡,所以沒有必要進行拷貝浪費效率,直接轉移資源即可,即直接交換數據(例如通過swap),如果是左值可能就需要進行深拷貝了。

3.5.4、右值引?和移動語義在傳參中的提效

查看STL?檔我們發現C++11以后容器的push和insert系列的接?否增加的右值引?版本。

例如:

  • 當實參是?個左值時,容器內部繼續調?拷?構造進?拷?,將對象拷?到容器空間中的對象。
  • 當實參是?個右值,容器內部則調?移動構造,右值對象的資源到容器空間的對象上。
  • 把我們之前模擬實現的 list 拷?過來,實現右值引?參數版本的push_back和insert。

實現代碼:

		//右值版本的insertiterator insert(iterator pos, T&& x){Node* cur = pos._node;Node* newnode = new Node(move(x));Node* prev = cur->_prev;// prev  newnode  curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}//右值版本的push_backvoid push_back(T&& x){insert(end(), move(x));}

這里有一個注意點:引用了右值的變量的屬性其實是左值,也就是說,對于一個變量,雖然它引用的是右值,但是他本身是左值,所以這里只是將這兩個方法的形參改為接收右值的形式是不夠的,例如:push_back的右值版本的方法中,如果只將形參改為能夠接收右值,其他不變,那么形參x引用的是右值,但x本身其實是左值,那么在方法內部調用insert方法時就會因為x是左值而調用左值對應的insert方法,而不是右值對應的insert方法,對于這種情況,我們有兩種方法解決,第一種是完美轉發,下面會介紹,這里使用第二種,因為這個方法內部我們是知道我們接收的是右值的,所以向下傳遞時將該變量move一下,將其轉換為右值,當然,這樣做所有對應調到的方法都要有右值版本才行,而且每次向下傳遞時都要move一下。

代碼完善:(僅右值相關部分)

		//節點的構造方法-右值版本ListNode(T&& data):_next(nullptr), _prev(nullptr), _data(move(data)){}

解釋:需要這個方法是因為在insert方法中通過右值直接構造節點。

其實這?還有?個emplace系列的接?,但是這個涉及可變參數模板,我們需要把可變參數模板講解以后再講解emplace系列的接?。

3.6類型分類

C++11以后,進?步對類型進?了劃分,右值被劃分純右值(pure value,簡稱prvalue)和將亡值 (expiring value,簡稱xvalue)。

純右值是指那些字?值常量或求值結果相當于字?值或是?個不具名的臨時對象。如:42、true、nullptr?或者類似?str.substr(1, 2)、str1 + str2?傳值返回函數調?,或者整形a、b,a++,a+b等。純右值和將亡值C++11中提出的,C++11中的純右值概念劃分等價于 C++98中的右值。(即內置類型的右值叫做純右值)

將亡值是指返回右值引?的函數的調?表達式和轉換為右值引?的轉換函數的調?表達,如 move(x)、static_cast(x)。(即自定義類型的右值叫做將亡值)

泛左值(generalized value,簡稱glvalue),泛左值包含將亡值和左值。

兩個關于值類型的中?和英?的官??檔,有興 趣可以了解細節:

https://zh.cppreference.com/w/cpp/language/value_categoryhttps://zh.cppreference.com/w/cpp/language/value_category

3.7、折疊引用

C++中不能直接定義引?的引?如 int& && r = i;,這樣寫會直接報錯,通過模板或 typedef?中的類型操作可以構成引?的引?。 通過模板或 typedef 中的類型操作構成引?的引?時,這時C++11給出了?個引?折疊的規則:右值引?的右值引?折疊成右值引?,所有其他組合均折疊成左值引?。

下?的程序中很好的展?了模板和typedef時構成引?的引?時的引?折疊規則

  • 像PerfectForward這樣的函數模板中,T&& x參數看起來是右值引?參數,但是由于引?折疊的規則,他傳遞左值時就是左值引?,傳遞右值時就是右值引?,有些地?也把這種函數模板的參數叫做萬能引?。
  • Function(T&& t)函數模板程序中,假設實參是int右值,模板參數T的推導int,實參是int左值,模板參數T的推導int&,再結合引?折疊規則,就實現了當實參是左值時,就實例化出左值引?版本形參的Function,實參是右值時,就實例化出右值引?版本形參的Function。

從上面代碼可以看到,無論是右值還是左值都可以調用該模版函數,且實際使用中,傳入左值就會生成左值版本的函數,傳入右值就會生成右值版本的函數,這就是引用折疊。對于函數而言,普通函數和使用了類的模版的成員函數都無法實現引用折疊,想要實現引用折疊,函數必須有自己的模版參數。例如:

上圖是稍加改動后的以前模擬實現的list,這個成員函數就只是右值版本的函數,不是引用折疊,因為它的模版是 list 這個類的,不是自己的,當類實例化時,這個模版參數就定下來了,他就沒法根據傳入的參數在去實例化不同的左值和右值的版本。

3.8、完美轉發

PerfectForward(T&& t)函數模板程序中,傳左值實例化以后是左值引?的PerfectForward函數,傳右值實例化以后是右值引?的PerfectForward函數。

但是結合我們在前面的講解,變量表達式都是左值屬性,也就意味著?個右值被右值引?綁定 后,右值引?變量表達式的屬性是左值,也就是說PerfectForward函數中t的屬性是左值,那么我們把t傳遞給下?層函數Fun,那么匹配的都是左值引?版本的Fun函數。這?我們想要保持t對象的屬性, 就需要使?完美轉發實現。

完美轉發聲明:

template <class T>?T&& forward (typename remove_reference<T>::type& arg);

template <class T>?T&& forward (typename remove_reference<T>::type&& arg);

完美轉發forward本質是?個函數模板,他主要還是通過引?折疊的?式實現,下??例中傳遞給 PerfectForward的實參是右值,T被推導為int,沒有折疊,forward內部t被強轉為右值引?返回;傳遞給 PerfectForward的實參是左值,T被推導為int&,引?折疊為左值引?,forward內部t被強轉為左值引?返回。

四、可變參數模版

4.1、基本語法及原理

C++11?持可變參數模板,也就是說?持可變數量參數的函數模板和類模板,可變數?的參數被稱 為參數包,存在兩種參數包:模板參數包,表?零或多個模板參數;函數參數包:表?零或多個函 數參數。

template <class? ...Args>?void Func(Args... args) {}

template <class? ...Args>?void Func(Args&... args) {}

template <class? ...Args>?void Func(Args&&... args) {}

我們?省略號來指出?個模板參數或函數參數的表??個包,在模板參數列表中,class...或 typename...指出接下來的參數表?零或多個類型列表;在函數參數列表中,類型名后?跟...指出 接下來表?零或多個形參對象列表;函數參數包可以?左值引?或右值引?表?,跟前?普通模板 ?樣,每個參數實例化時遵循引?折疊規則。

可變參數模板的原理跟模板類似,本質還是去實例化對應類型和個數的多個函數。

這?我們可以使?sizeof...(參數包)方式去計算參數包中參數的個數。

示例代碼一:

void Print()
{cout << endl;
}//參數包傳入時第一個參數解析到x中
//然后再遞歸依次進行解析
template <class T, class ...Args>
void Print(T&& x, Args&&... args)
{//不可以這樣結束遞歸//因為這樣是運行時結束遞歸的方式//但是解析參數包時編譯時解析的,所以需要提供一個空參的函數來結束編譯時遞歸//if (sizeof...(args) == 0)//	return;cout << x << " ";Print(args...);
}// 可變模版參數
// 參數類型可變
// 參數個數可變
template <class ...Args>
void ShowList(Args... args)
{//計算參數包的參數個數cout << sizeof...(args) << endl;// 可變參數模版在編譯時解析
// 下面是運行獲取和解析,所以不支持這樣用
//	/*cout << sizeof...(args) << endl;
//	for (size_t i = 0; i < sizeof...(args); i++)
//	{
//		cout << args[i] << " ";
//	}//正確解析方式Print(args...);
}int main()
{//下面這些調用方式都可以ShowList();ShowList(1);ShowList(1, "xxxxx");ShowList(1, "xxxxx", 2.2);return 0;
}

示例代碼二:

template <class T>
int PrintArg(T t)
{cout << t << " ";//借助數組解析,但是返回值會被放入數組中//所以返回int類型的0,符合數組存儲數據的類型//當然也可以不返回該類型的數據,那就需要用到逗號表達式return 0;
}template <class ...Args>
void ShowList(Args... args)
{//借助數組來幫助我們解析參數包//返回值會放到數組中int arr[] = { PrintArg(args)... };//如果函數返回值和數組類型不相符可以這樣// 通過逗號表達式,最終放入數組中的是0//int arr[] = { (PrintArg(args),0)...};cout << endl;
}//編譯推演生成下面的函數
//void ShowList(int x, char y, std::string z)
//{
//	int arr[] = { PrintArg(x),PrintArg(y),PrintArg(z) };
//	cout << endl;
//}//這樣也可以
//template <class ...Args>
//void ShowList(Args... args)
//{
// //這里必須用逗號表達式,因為cout返回的類型是ostream的
//	int arr[] = { (cout<<(args)<<" ", 0)...};
//
//	cout << endl;
//}int main()
{ShowList(1, 'A', std::string("sort"));return 0;
}

4.2、包擴展

對于?個參數包,我們除了能計算他的參數個數,我們能做的唯?的事情就是擴展它,當擴展?個 包時,我們還要提供?于每個擴展元素的模式,擴展?個包就是將它分解為構成的元素,對每個元 素應?模式,獲得擴展后的列表。我們通過在模式的右邊放?個省略號(...)來觸發擴展操作。底層 的實現細節如圖1所?。

C++還?持更復雜的包擴展,直接將參數包依次展開依次作為實參給?個函數去處理。

4.3、emplace系列接口

template <class...? Args> void emplace_back (Args&&...? args);

template <class...? Args> iterator emplace (const_iterator position, Args&&...? args);

  • C++11以后STL容器新增了empalce系列的接?,empalce系列的接?均為模板可變參數,功能上兼容push和insert系列,但是empalce還?持新玩法,假設容器為container,empalce還?持直接插?構造T對象的參數,這樣有些場景會更?效?些,可以直接在容器空間上構造T對象。
  • emplace_back總體??是更?效,推薦以后使?emplace系列替代insert和push系列。
  • 第?個程序中我們模擬實現了list的emplace和emplace_back接?,這?把參數包不段往下傳遞, 最終在結點的構造中直接去匹配容器存儲的數據類型T的構造,所以達到了前?說的empalce?持直接插?構造T對象的參數,這樣有些場景會更?效?些,可以直接在容器空間上構造T對象。
  • 傳遞參數包過程中,如果是Args&&...? args的參數包,要?完美轉發參數包,?式如下:std::forward<Args>(args)...,否則編譯時包擴展后右值引?變量表達式就變成了左值。

示例代碼一:(使用)

// emplace_back總體而言是更高效,推薦使用
int main()
{//基本使用和push_xxx/insert方法類似//這里使用的是前面我們自己實現的string,方便我們看效果list<bit::string> lt;// 左值bit::string s1("111111111111");lt.emplace_back(s1);// 右值lt.emplace_back(move(s1));list<pair<bit::string, int>> lt1;// 構造pair + 拷貝/移動構造pair到list的節點中data上pair<bit::string, int> kv("蘋果", 1);lt1.emplace_back(kv);lt1.emplace_back(move(kv));cout << endl << endl;// 不同點// 直接把構造pair參數包往下傳,直接用pair參數包構造pairlt1.emplace_back("蘋果", 1);cout << endl;// 直接把構造string參數包往下傳,直接用string參數包構造stringlt.emplace_back("111111111111");//使用的構造函數:string(int n, const char ch)lt.emplace_back(10, 'x');cout << endl << endl;return 0;
}

效果:

示例代碼二:(emplace_back實現)

		//節點的構造函數  ListNodetemplate<class... Args>ListNode(Args... args):_next(nullptr), _prev(nullptr), _data(forward<Args>(args)...){}//listtemplate<class... Args>iterator insert(iterator pos, Args&&... args){Node* cur = pos._node;Node* newnode = new Node(forward<Args>(args)...);Node* prev = cur->_prev;// prev  newnode  curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}template<class... Args>void emplace_back(Args&&... args){insert(end(), forward<Args>(args)...);}

五、新的類功能

5.1、默認的移動構造和移動賦值

  • 原來C++類中,有6個默認成員函數:構造函數/析構函數/拷?構造函數/拷?賦值重載/取地址重 載/const 取地址重載,最后重要的是前4個,后兩個?處不?,默認成員函數就是我們不寫編譯器 會?成?個默認的。C++11 新增了兩個默認成員函數,移動構造函數和移動賦值運算符重載。
  • 如果你沒有??實現移動構造函數,且沒有實現析構函數、拷?構造、拷?賦值重載中的任意? 個。那么編譯器會?動?成?個默認移動構造。默認?成的移動構造函數,對于內置類型成員會執?逐成員按字節拷?,?定義類型成員,則需要看這個成員是否實現移動構造,如果實現了就調?移動構造,沒有實現就調?拷?構造。
  • 如果你沒有??實現移動賦值重載函數,且沒有實現析構函數、拷?構造、拷?賦值重載中的任意?個,那么編譯器會?動?成?個默認移動賦值。默認?成的移動賦值函數,對于內置類型成員會執?逐成員按字節拷?,?定義類型成員,則需要看這個成員是否實現移動賦值,如果實現了就調?移動賦值,沒有實現就調?拷?賦值。(默認移動賦值跟上?移動構造完全類似)。
  • 如果你提供了移動構造或者移動賦值,編譯器不會?動提供拷?構造和拷?賦值。所以如果強制生成默認的移動構造和移動賦值,就必須主動實現拷貝構造和拷貝賦值。

5.2、defult和delete

  • C++11可以讓你更好的控制要使?的默認函數。假設你要使?某個默認的函數,但是因為?些原因這個函數沒有默認?成。?如:我們提供了拷?構造,就不會?成移動構造了,那么我們可以使?default關鍵字強制移動構造的?成。
  • 如果能想要限制某些默認函數的?成,在C++98中,是該函數設置成private,并且只聲明不定義, 這樣只要其他?想要調?就會報錯。在C++11中更簡單,只需在該函數聲明加上=delete即可,該語法指?編譯器不?成對應函數的默認版本,稱=delete修飾的函數為刪除函數。

六、lambda

6.1、lambda表達式語法

lambda 表達式本質是?個匿名函數對象,跟普通函數不同的是他可以定義在函數內部。

lambda 表達式語法使?層??沒有類型,所以我們?般是?auto或者模板參數定義的對象去接收 lambda 對象。

lambda表達式的格式: [capture-list] (parameters)-> return type { function boby }:

  • [capture-list] :捕捉列表,該列表總是出現在 lambda 函數的開始位置,編譯器根據 [ ] 來判斷接下來的代碼是否為 lambda 函數,捕捉列表能夠捕捉上下?中的變量供 lambda 函數使?,捕捉列表可以傳值和傳引?捕捉,捕捉列表為空也不能省略。
  • (parameters) :參數列表,與普通函數的參數列表功能類似,如果不需要參數傳遞,則可以連同()?起省略。
  • ->return type :返回值類型,?追蹤返回類型形式聲明函數的返回值類型,沒有返回值時此部分可省略。?般返回值類型明確情況下,也可省略,由編譯器對返回類型進?推導。
  • {function boby} :函數體,函數體內的實現跟普通函數完全類似,在該函數體內,除了可以使?其參數外,還可以使?所有捕獲到的變量,函數體為空也不能省略。

6.2、捕捉列表

lambda 表達式中默認只能? lambda 函數體和參數中的變量,如果想?外層作?域中的變量就需要進?捕捉。捕獲的三種方式如下:

  • 第?種捕捉?式是在捕捉列表中顯?的傳值捕捉和傳引?捕捉,捕捉的多個變量?逗號分割。[x, y,&z] 表?x和y值捕捉,z引?捕捉。
  • 第?種捕捉?式是在捕捉列表中隱式捕捉,我們在捕捉列表寫?個=表?隱式值捕捉,在捕捉列表寫?個&表?隱式引?捕捉,這樣我們 lambda 表達式中?了那些變量,編譯器就會?動捕捉那些變量。
  • 第三種捕捉?式是在捕捉列表中混合使?隱式捕捉和顯?捕捉。[=, &x]表?其他變量隱式值捕捉, x引?捕捉;[&, x, y]表?其他變量引?捕捉,x和y值捕捉。當使?混合捕捉時,第?個元素必須是 &或=,并且&混合捕捉時,后?的捕捉變量必須是值捕捉,同理=混合捕捉時,后?的捕捉變量必須是引?捕捉。

捕獲列表詳細說明:

捕獲列表描述了上下文中哪些數據可以被lambda使用,以及使用的方式是傳值還是傳引用。

[var]:表示值傳遞方式捕獲變量var。默認會被const修飾

[=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)。

[&var]:表示引用傳遞捕獲變量var。

[&]:表示引用傳遞捕獲所有父作用域中的變量(包括this)。

?lambda 表達式如果在函數局部域中,他可以捕捉 lambda 位置之前定義的變量,不能捕捉靜態局部變量和全局變量,靜態局部變量和全局變量也不需要捕捉, lambda 表達式中可以直接使?。這也意味著 lambda 表達式如果定義在全局位置,捕捉列表必須為空。

默認情況下, lambda 捕捉列表是被const修飾的,也就是說傳值捕捉的過來的對象不能修改, mutable加在參數列表的后?可以取消其常量性,也就說使?該修飾符后,傳值捕捉的對象就可以 修改了,但是修改還是形參對象,不會影響實參。使?該修飾符后,參數列表不可省略(即使參數為空)。

6.3、lambda的應?

在學習 lambda 表達式之前,我們的使?的可調?對象只有函數指針和仿函數對象,函數指針的類型定義起來?較麻煩,仿函數要定義?個類,相對會?較麻煩。使? lambda 去定義可調?對象,既簡單??便。

示例代碼一:

// lambda 可以理解為定義了一個匿名函數的對象
int main()
{//lambda表達式沒有類型,需要使用auto推導auto add1 = [](int x, int y)->int {return x + y; };cout << add1(1, 2) << endl;//多行時可以這樣寫auto func1 = []()->int{cout << "hello bit" << endl;cout << "hello world" << endl;return 0;};func1();// 返回值類型可自動推導類型,所以可以省略auto func2 = []{cout << "hello bit" << endl;cout << "hello world" << endl;return 0;};cout << func2() << endl;//無形參是也可以省略auto func3 = []{cout << "hello bit" << endl;cout << "hello world" << endl;};func3();return 0;
}

示例代碼二:

int main()
{int a = 0, b = 1;auto swap1 = [](int& x, int& y){// 只能用當前lambda局部域(即形參)和捕捉的對象(捕獲列表)int tmp = x;x = y;y = tmp;};swap1(a, b);// 傳值捕捉本質是一種拷貝,并且const修飾了// mutable相當于去掉const屬性,可以修改了// 但是修改了不會影響外面被捕捉的值,因為是一種拷貝auto swap2 = [a, b]()mutable{int tmp = a;a = b;b = tmp;};swap2();//如果想在lambda表達式中影響外部的值,可以傳引用捕獲//傳引用捕獲的值不會被const修飾,在表達式內可以直接修改auto swap3 = [&a, &b](){int tmp = a;a = b;b = tmp;};swap3();return 0;
}

示例代碼三:

int x = 0;int main()
{// 只能用當前lambda局部域和捕捉的對象// 全局對象和靜態變量不需要捕獲,直接就可以使用int a = 0, b = 1, c = 2, d = 3;// 所有值傳值捕捉auto func1 = [=]{int ret = a + b + c + d + x;return ret;};// 所有值傳引用捕捉auto func2 = [&]{a++;b++;c++;d++;int ret = a + b + c + d;return ret;};// 混合捕捉auto func3 = [&a, b]{a++;// b++;int ret = a + b;return ret;};// 混合捕捉// 所有值以引用方式捕捉,d用傳值捕捉auto func4 = [&, d]{a++;b++;c++;//d++;int ret = a + b + c + d;};//所有值以傳值方式捕獲,d傳引用捕獲auto func5 = [=, &d]() mutable{a++;b++;c++;d++;int ret = a + b + c + d;};return 0;
}

lambda 在很多其他地??起來也很好?。?如線程中定義線程的執?函數邏輯,智能指針中定制刪除器等, lambda 的應?還是很?泛的,以后我們會不斷接觸到。

6.4、lambda的原理

lambda 的原理和范圍for很像,編譯后從匯編指令層的?度看,壓根就沒有 lambda 和范圍for 這樣的東西。范圍for底層是迭代器,?lambda底層是仿函數對象,也就說我們寫了?個lambda 以后,編譯器會?成?個對應的仿函數的類。

仿函數的類名是編譯按?定規則?成的,保證不同的 lambda ?成的類名不同,lambda參數/返回類型/函數體就是仿函數operator()的參數/返回類型/函數體, lambda 的捕捉列表本質是?成的仿函數類的成員變量,也就是說捕捉列表的變量都是 lambda 類構造函數的實參,當然隱式捕捉,編譯器要看使?哪些就傳那些對象。

七、包裝器

7.1、function

  • std::function 是?個類模板,也是?個包裝器。 std::function 的實例對象可以包裝,存儲其他的可調?對象,包括函數指針、仿函數、 lambda 、 bind 表達式等,存儲的可調?對象被稱為std::function 的?標。若 std::function 不含?標,則稱它為空。調?空 std::function 的?標導致拋出 std::bad_function_call 異常。
  • ?function的官??件鏈接:function - C++ Reference

下面是 function 的原型,他被定義<functional>頭?件中:

函數指針、仿函數、 lambda 等可調?對象的類型各不相同, std::function 的優勢就是統?類型,對他們都可以進?包裝,這樣在很多地?就?便聲明可調?對象的類型。

語法: function<返回值類型(形參類型)> 變量名 = 封裝的可調用對象

示例代碼:

#include<functional>int f(int a, int b)
{return a + b;
}struct Functor
{
public:int operator() (int a, int b){return a + b;}
};class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};int main()
{// 包裝可調用對象//函數function<int(int, int)> f1 = f;//仿函數function<int(int, int)> f2 = Functor();//lambda表達式function<int(int, int)> f3 = [](int a, int b) {return a + b; };cout << f1(1, 1) << endl;cout << f2(1, 1) << endl;cout << f3(1, 1) << endl;// 包裝靜態成員函數//靜態成員函數不用取地址符也可以取到地址,所以這里不寫&也可以function<int(int, int)> f4 = &Plus::plusi;cout << f4(1, 1) << endl;// 包裝非靜態成員函數//類的非靜態成員函數必須有取地址符才能取到地址//因為非靜態成員函數有隱含的this指針,所以需要多傳一個參數//這個參數可以是類類型的指針,也可以是類對象//指針版function<double(Plus*, double, double)> f5 = &Plus::plusd;Plus pd;cout << f5(&pd, 1.1, 1.1) << endl;// 類對象版function<double(Plus, double, double)> f6 = &Plus::plusd;cout << f6(pd, 1.1, 1.1) << endl;cout << f6(Plus(), 1.1, 1.1) << endl;return 0;
}

下?的代碼樣例展?了 std::function 作為map的參數,實現字符串和可調?對象的映射表功能。

// 這種?式的最?優勢之?是?便擴展,假設還有其他運算,我們增加map中的映射即可 
class Solution 
{
public:int evalRPN(vector<string>& tokens) {stack<int> st;// function作為map的映射可調?對象的類型 map<string, function<int(int, int)>> opFuncMap = {{"+", [](int x, int y) {return x + y; }},{"-", [](int x, int y) {return x - y; }},{"*", [](int x, int y) {return x * y; }},{"/", [](int x, int y) {return x / y; }}};for (auto& str : tokens){if (opFuncMap.count(str)) // 操作符 {int right = st.top();st.pop();int left = st.top();st.pop();int ret = opFuncMap[str](left, right);st.push(ret);}else{st.push(stoi(str));}}return st.top();}
};

7.2、bind

  • bind 是?個函數模板,它也是?個可調?對象的包裝器,可以把他看做?個函數適配器,對接收的fn可調?對象進?處理后返回?個可調?對象。 bind 可以?來調整參數個數和參數順序。
  • bind 也在<functional>這個頭?件中。
  • 調?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為第?個參數,以此類推。_1/_2/_3....這些占位符放到placeholders的?個命名空間中。

bind聲明:

???????placeholders聲明:

示例代碼:

//這里它們從命名空間中放開
//否則指定命名空間的方式寫起來太長了
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;int Sub(int a, int b)
{return (a - b) * 10;
}int SubX(int a, int b, int c)
{return (a - b - c) * 10;
}int main()
{auto sub1 = bind(Sub, _1, _2);cout << sub1(10, 5) << endl;// bind 本質返回的一個仿函數對象// 調整參數順序(不常用)// _1代表第一個實參// _2代表第二個實參// ...以此類推auto sub2 = bind(Sub, _2, _1);cout << sub2(10, 5) << endl;// 調整參數個數 (常用)//直接將第一個參數固定了auto sub3 = bind(Sub, 100, _1);cout << sub3(5) << endl;auto sub4 = bind(Sub, _1, 100);cout << sub4(5) << endl;// 分別綁死第123個參數auto sub5 = bind(SubX, 100, _1, _2);cout << sub5(5, 1) << endl;auto sub6 = bind(SubX, _1, 100, _2);cout << sub6(5, 1) << endl;auto sub7 = bind(SubX, _1, _2, 100);cout << sub7(5, 1) << endl;function<double(Plus, double, double)> f6 = &Plus::plusd;Plus pd;cout << f6(pd, 1.1, 1.1) << endl;cout << f6(Plus(), 1.1, 1.1) << endl;// bind一般用于,綁死一些固定參數function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);cout << f7(1.1, 1.1) << endl;//auto func1 = [](double rate, double monty, int year)->double {return monty * rate * year;};auto func1 = [](double rate, double monty, int year)->double {double ret = monty;for (int i = 0; i < year; i++){ret += ret * rate;}return ret - monty;};function<double(double)> func3_1_5 = bind(func1, 0.015, _1, 3);function<double(double)> func5_1_5 = bind(func1, 0.015, _1, 5);function<double(double)> func10_2_5 = bind(func1, 0.025, _1, 10);function<double(double)> func20_3_5 = bind(func1, 0.035, _1, 30);cout << func3_1_5(1000000) << endl;cout << func5_1_5(1000000) << endl;cout << func10_2_5(1000000) << endl;cout << func20_3_5(1000000) << endl;return 0;
}

圖解:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/78135.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/78135.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/78135.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于機器學習的網絡釣魚郵件智能檢測與防護系統

phishingDP 介紹 phishingDP 是一個基于機器學習的網絡釣魚郵件智能檢測與防護系統&#xff0c;旨在通過深度學習技術識別潛在的釣魚郵件&#xff0c;保護用戶免受網絡詐騙威脅。該系統集成了數據預處理、模型訓練、實時預測和結果可視化功能&#xff0c;提供用戶友好的Web界…

OpenAI 推出「輕量級」Deep Research,免費用戶同享

剛剛&#xff0c;OpenAI 正式上線了面向所有用戶的「輕量級」Deep Research 版本&#xff0c;意味著即便沒有付費訂閱&#xff0c;也能體驗這一強大工具的核心功能。 核心差異&#xff1a;o4-mini vs. o3 模型迭代 傳統的深度研究功能基于更大規模的 o3 模型。輕量級版本則改以…

什么是優質的靜態IP?以及如何選擇優質的靜態IP?

在如今的大數據生態中&#xff0c;靜態IP的使用頻率和重要性不斷提升。但是&#xff0c;我們常聽到業界提到“優質的靜態IP”&#xff0c;那么什么樣的靜態IP能夠稱之為優質&#xff1f;如何判斷這些IP能否滿足我們的需求&#xff1f;今天這篇文章&#xff0c;將為您揭開優質靜…

Hadoop生態圈框架部署 - Windows上部署Hadoop

文章目錄 前言一、下載Hadoop安裝包及bin目錄1. 下載Hadoop安裝包2. 下載Hadoop的bin目錄 二、安裝Hadoop1. 解壓Hadoop安裝包2. 解壓Hadoop的Windows工具包 三、配置Hadoop1. 配置Hadoop環境變量1.1 打開系統屬性設置1.2 配置環境變量1.3 驗證環境變量是否配置成功 2. 修改Had…

搜廣推校招面經八十一

OPPO搜廣推一面面經 一、介紹一下PLE模型 在多任務學習&#xff08;Multi-Task Learning, MTL&#xff09;中&#xff0c;多個任務共享部分模型結構&#xff0c;以提升整體效果。然而&#xff0c;不同任務間存在 任務沖突&#xff08;Task Conflict&#xff09; 問題&#xf…

LangChain 中主流的 RAG 實現方式

文章目錄 **一、基礎流程實現**1. **全自動索引構建&#xff08;VectorstoreIndexCreator&#xff09;**2. **標準問答鏈&#xff08;RetrievalQA&#xff09;**3. **Document Chain 手動檢索**4. **load_qa_chain&#xff08;傳統方式&#xff09;** **二、高級定制化實現**1…

解決:springmvc工程 響應時,將實體類對象 轉換成json格式數據

問題&#xff1a;一直無法將user對象轉成json格式 按理來說&#xff0c;我在類上使用RestController注解&#xff0c;就可以實現將實體類對象寫入響應體中&#xff0c;并作為json格式傳遞到客戶端&#xff0c;但現實是沒有生效&#xff0c;并且出現404&#xff0c;406&#xf…

【踩坑記錄】stm32 jlink程序燒錄不進去

最近通過Jlink給STM32燒寫程序時一直報錯&#xff0c;但是換一個其他工程就可以燒錄&#xff0c;對比了一下jink配置&#xff0c;發現是速率選太高了“SW Device”&#xff0c;將燒錄速率調整到10MHz以下就可以了

運維打鐵:Mysql 分區監控以及管理

文章目錄 一、簡介二、設計邏輯1、配置文件檢查2、創建邏輯3、 刪除邏輯4、重建表分區邏輯5、recognize maxvalue分區表邏輯6、創建多個未來分區邏輯7、定時檢測分區是否創建成功&#xff0c;否則發送告警郵件。 三、解決的問題四、配置例子與介紹 一、簡介 操作數據庫&#xf…

Appium自動化開發環境搭建

自動化 文章目錄 自動化前言 前言 Appium是一款開源工具&#xff0c;用于自動化iOS、Android和Windows桌面平臺上的本地、移動web和混合應用程序。原生應用是指那些使用iOS、Android或Windows sdk編寫的應用。移動網頁應用是通過移動瀏覽器訪問的網頁應用(appum支持iOS和Chrom…

《R語言SCI期刊論文繪圖專題計劃》大綱

今天開始&#xff0c;我將和大家分享系統且詳細的《R語言SCI期刊繪圖專題教程》&#xff0c;內容會從基礎到高階應用&#xff0c;從配色美學到頂刊風格復現&#xff0c;確保大家可以學到高質量內容&#xff01;下面是大綱。 &#x1f4da;《R語言SCI期刊論文繪圖專題計劃》 第…

STUN協議 與 TURN協議

STUN&#xff08;Session Traversal Utilities for NAT&#xff0c;NAT會話穿越應用程序&#xff09;是一種網絡協議&#xff0c; STUN&#xff08;Simple Traversal of User Datagram Protocol through Network Address Translators (NATs)&#xff0c;NAT的UDP簡單穿越&#…

在vscode終端中運行npm命令報錯

解決方案 這個錯誤信息表明&#xff0c;你的系統&#xff08;可能是 Windows&#xff09;阻止了 PowerShell 執行腳本&#xff0c;這是由于 PowerShell 的執行策略導致的。PowerShell 的執行策略控制著在系統上運行哪些 PowerShell 腳本。默認情況下&#xff0c;Windows 可能…

手搓雷達圖(MATLAB)

看下別人做出來什么效果 話不多說&#xff0c;咱們直接開始 %% 可修改 labels {用戶等級, 發帖數, 發帖頻率, 點度中心度, 中介中心度, 帖子類型計分, 被列為提案數}; cluster_centers [0.8, 4.5, 3.2, 4.0, 3.8, 4.5, 4.2; % 核心用戶0.2, 0.5, 0.3, 0.2, 0.1, 0.0, 0.0;…

ViViT: 一種視頻視覺Transformer

摘要 我們提出了基于純transformer的視頻分類模型,借鑒了這種模型在圖像分類中的成功經驗。我們的模型從輸入視頻中提取時空token,然后通過一系列transformer層進行編碼。為了處理視頻中遇到的長序列token,我們提出了幾種高效的模型變種,這些變種將輸入的空間和時間維度進…

嵌入式鴻蒙系統環境搭建與配置要求實現01

各位開發者大家好,今天主要給大家分享一下,鴻蒙系統的環境配置實現。 第一:鴻蒙配置基本要求 對電腦的要求,虛擬機配置建議 200GB 硬盤大小,10GB 內存,4*2CPU。 安裝必要的依賴文件方法: sudo apt-get update && sudo apt-get install binutils git git-lfs g…

【多目標進化算法】常見多目標進化算法一覽

算法全稱核心特點備注NSGA-IINon-dominated Sorting Genetic Algorithm II非支配排序 擁擠度最經典&#xff0c;應用最廣NSGA-IIINon-dominated Sorting Genetic Algorithm III支撐向量引導&#xff0c;適合高維&#xff08;3目標以上&#xff09;NSGA-II 的高維擴展版MOEA/DM…

創意無限,從這些視頻素材開始你的創作!

在視頻創作的世界里&#xff0c;找到合適的素材就像是挖掘寶藏&#xff0c;不僅能節省時間&#xff0c;還能讓作品瞬間提升一個檔次。今天&#xff0c;就來給大家分享一些超實用的視頻素材網站&#xff0c;無論是國內的寶藏平臺&#xff0c;還是國外的優質資源&#xff0c;都能…

QT創建新項目(13)

文章目錄 一、本章說明二、QT組件簡介及相關筆記三、項目創建四、QT學習建議一、本章說明 注:本節為【基于STM的環境監測系統(節點+云服務器存儲+QT界面設計)】項目第13篇文章,前面已安裝了QT軟件,本章主要介紹新項目創建及注意事項,QT的初學者相關學習資料 二、QT組件…

Langgraph實戰-Agent-ReAct(Reason+Act)概述

Langgraph實戰-Agent-ReAct&#xff08;ReasonAct&#xff09;概述 概述 ReAct 架構將推理與動作相結合&#xff0c;使Agent能夠通過生成想法并基于這些想法執行動作。這種決策透明度使Agent能夠更負責地執行任務&#xff0c;因為它會記錄每一步的推理過程。 這種架構最適合…