中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

深圳政府在線網站石家莊網絡seo推廣

深圳政府在線網站,石家莊網絡seo推廣,群暉 wordpress 升級,定西做網站目錄 1. 列表初始化initializer_list 2. 前面提到的一些知識點 2.1 小語法 2.2 STL中的一些變化 3. 右值和右值引用 3.1 右值和右值引用概念 3.2 右值引用類型的左值屬性 3.3 左值引用與右值引用比較 3.4 右值引用的使用場景 3.4.1 左值引用的功能和短板 3.4.2 移動…

目錄

1.?列表初始化initializer_list

2. 前面提到的一些知識點

2.1 小語法

2.2 STL中的一些變化

3. 右值和右值引用

3.1?右值和右值引用概念

3.2?右值引用類型的左值屬性

3.3?左值引用與右值引用比較

3.4?右值引用的使用場景

3.4.1 左值引用的功能和短板

3.4.2?移動構造

3.4.3?移動賦值

3.4.4?插入右值時減少深拷貝

4. 完美轉發(fā)

4.1?萬能引用(引用折疊)

4.2 完美轉發(fā)

5. 新的類功能

5.1 默認生成的移動構造/賦值

5.2 類里新的關鍵字

本篇完。


在2003年C++標準委員會曾經提交了一份技術勘誤表(簡稱TC1),使得C++03這個名字已經取代了C++98稱為C++11之前的最新C++標準名稱。不過由于C++03(TC1)主要是對C++98標準中的漏洞進行修復,語言的核心部分則沒有改動,因此人們習慣性的把兩個標準合并稱為C++98 / 03標準。從C++0x到C++11,C++標準10年磨一劍,第二個真正意義上的標準珊珊來遲。相比于C++98 / 03,C++11則帶來了數(shù)量可觀的變化,其中包含了約140個新特性,以及對C++03標準中約600個缺陷的修正,這使得C++11更像是從C++98 / 03中孕育出的一種新語言。相比較而言,C++11能更好地用于系統(tǒng)開發(fā)和庫開發(fā)、語法更加泛華和簡單化、更加穩(wěn)定和安全,不僅功能更強大,而且能提升程序員的開發(fā)效率,公司實際項目開發(fā)中也用得比較多,所以我們要作為一個重點去學習。C++11增加的語法特性非常篇幅非常多,我們這里沒辦法一 一講解,所以本主要講解實際中比較實用的語法。

1.?列表初始化initializer_list

  • 列表:花括號:{ }就被叫做列表。

我們之前可以使用列表來初始化數(shù)組,初始化結構體變量,初始化元素類型為結構體變量的數(shù)組等等。

  • C++11擴大了用大括號括起的列表(初始化列表)的使用范圍,使其可用于所有的內置類型和用戶自定義的類型,使用初始化列表時,可添加等號(=),也可不添加。
#include <iostream>
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;}protected:int _year = 1;int _month = 1;int _day = 1;
};int main()
{int x1 = 1;int x2 = { 2 }; // 要能看懂,但是不建議使用int x3{ 2 };Date d1(2023, 1, 1); // 都是在調用構造函數(shù)Date d2 = { 2023, 2, 2 }; // 要能看懂,但是不建議使用Date d3{ 2023, 3, 3 };return 0;
}

可以不加等號進行初始化,如上圖代碼所示,但是強烈不建議使用。

這其實很雞肋,沒有什么價值,繼續(xù)使用C++98中的方式就挺好的,而且容易理解,C++11中的方式反而不太好理解了。C++中這種雞肋的語法被很多人吐槽,理性看待。

列表初始化真正有意義的地方是用于初始化STL中的容器:

之前提到:vector和list以及map等STL中的容器也可以像普通數(shù)組一樣使用初始化列表來初始化了。這是因為列表初始化本身就是一個類模板:

如上圖所示,這是C++11才有的一個類型,該類型叫做列表初始化,而且還有自己的成員函數(shù),包括構造函數(shù),計算列表大小的接口,獲取列表迭代器位置。(但幾乎都不用)

C++11為這些容器提供了新的構造函數(shù),該構造函數(shù)是使用列表來初始化對象的,它的形參就是initializer_list,所以列表初始化才可以初始化STL中的容器。

賦值運算符重載函數(shù)也有一個列表的重載版本:

#include <iostream>
#include <vector>
#include <list>
#include <map>
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;}protected:int _year = 1;int _month = 1;int _day = 1;
};int main()
{int x1 = 1;int x2 = { 2 }; // 要能看懂,但是不建議使用int x3{ 2 };Date d1(2023, 1, 1); // 都是在調用構造函數(shù)Date d2 = { 2023, 2, 2 }; // 要能看懂,但是不建議使用Date d3{ 2023, 3, 3 };// 調用支持list (initializer_list<value_type> il)類似這樣的構造函數(shù)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; // 打印初始化列表的類型vector<Date> v3 = {d1, d2, d3};vector<Date> v4 = { { 2022, 1, 1 }, {2022, 11, 11} };string s1 = "11111";map<string, string> dict = { { "sort", "排序" }, { "insert", "插入" } }; // 構造initializer_list<pair<const string, string>> kvil = { { "left", "左邊" }, { "right", "右邊" } }; // 賦值重載dict = kvil; // 上面的類型就不能用auto推導,編譯器不知道那里是一個pairreturn 0;
}

2. 前面提到的一些知識點

2.1 小語法

C++11提供了一些新的小語法,很多我們都接觸過甚至是使用過。

c++11提供了多種簡化聲明的方式,尤其是在使用模板時。這里講autodecltype

auto:這個關鍵字我們已經使用過很多了

在C++98中auto是一個存儲類型的說明符,表明變量是局部自動存儲類型,但是局部域中定義局部的變量默認就是自動存儲類型,所以auto就沒什么價值了。C++11中廢棄auto原來的用法,將其用于實現(xiàn)自動類型推斷。這樣要求必須進行顯示初始化,讓編譯器將定義對象的類型設置為初始化值的類型。

#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
#include <map>
using namespace std;int main()
{int i = 10;auto p = &i;auto pf = strcpy;cout << typeid(p).name() << endl;cout << typeid(pf).name() << endl;map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };//map<string, string>::iterator it = dict.begin();auto it = dict.begin();return 0;
}

decltype:

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

使用typeid().name()只能打印出類型的名稱,并不能用這個名稱繼續(xù)創(chuàng)建變量,而decltype可以:

template<class T1, class T2>
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret;cout << typeid(ret).name() << endl;
}int main()
{const int x = 1;double y = 2.2;decltype(x * y) ret; // ret的類型是doubledecltype(&x) p; // p的類型是int*cout << typeid(ret).name() << endl;cout << typeid(p).name() << endl;F(1, 'a');return 0;
}

使用decltype可以自動推演類型,并且可以用推演出的結果繼續(xù)創(chuàng)建變量,如上圖所示,對于一些不同類型直接的運算結果,decltype有奇效。

nullptr:

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

在C++中存在條件編譯:(以后用nullptr就行了)這算是修復了一個bug

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL  ((void*)0)
#endif
#endif

范圍for循環(huán)

范圍for我們也一直都在使用,這是C++11提供的語法糖,使用起來非常方便,它的底層就是迭代器,只是編譯器給自動替換了,這里就不再講解了。

2.2 STL中的一些變化

新容器:

紅色框中的是C++11增加的新容器,基本只有unordered_map和unordered_set有用,其他很雞肋。容器array對標的是靜態(tài)數(shù)組,array也是一個靜態(tài)的,也就是在棧區(qū)上的,大小是通過一個非類型模板參數(shù)確定的。容器forward_list是一個單鏈表,也很雞肋,因為絕大部分場景雙鏈表都可以滿足要求,而且更加方便,唯一使用到單鏈表的地方就是哈希桶中。前面都提到過。

至于unordered_map和unordered_set,這兩個容器的底層是哈希桶,雖然不能實現(xiàn)排序,但是可以降重。而且在查找時具有其他容器無法比擬的效率。這兩個容器是非常實用的,而且也是我們經常使用的。

容器中的使用新方法:

1. 使用列表構造
在前面就講解過了,幾乎每個容器都增加了新的接口,使用std::initializer_list類型來構造。

2. 移動構造和移動賦值
在下面講解了右值引用就可以明白了。

3. emplace_xxx插入接口或者右值引用版本的插入接口。
同樣在后面才能學習到。

3. 右值和右值引用

傳統(tǒng)的C++語法中就有引用的語法,而C++11中新增了的右值引用語法特性,
之前學習的引用就叫做左值引用。無論左值引用還是右值引用,都是給對象取別名。

3.1?右值和右值引用概念

什么是左值?什么是右值?

  • 左值:一個表示數(shù)據(jù)的表達式,如變量名或者指針解引用。
  • 特點:可以對左值取地址 + 可以對左值賦值。

?上圖代碼中所示的變量都屬于左值,要牢記左值可以取地址這一個特性。

  • ?定義時const修飾符后的左值,不能給他賦值,但是可以取它的地址。
  • ?左值可以出現(xiàn)在賦值符號的左邊,也可以出現(xiàn)在賦值符號的右邊。

  • 右值:也是一個表示數(shù)據(jù)的表達式。如:字面常量,表達式返回值,函數(shù)返回值,類型轉換時的臨時變量等等。
  • 特點:右值不可以取地址,不可以賦值。

要牢記右值特性:不能取地址不能賦值。

  • ?右值可以出現(xiàn)在賦值符號的右邊,但是不能出現(xiàn)出現(xiàn)在賦值符號的左邊。

什么是右值引用?

左值引用是給左值取別名,右值引用顯而易見就是給右值取別名。

  • 右值引用使用兩個&符號。

?上圖代碼中的rr1,rr2,rr3就是三個右值的別名,也就是右值引用。

3.2?右值引用類型的左值屬性

  • 右值是不能取地址的,但是給右值取別名后,會導致右值被存儲到特定位置,且可以取到該位置的地址。

對于內置類型的右值,如字面常量,一旦右值引用以后,就會被存儲到特定的位置,

并且可以取到該地址,而且還可以修改。

int main()
{int&& rr1 = 10;cout << rr1 << endl;rr1 = 5;cout << rr1 << endl;const double&& rr2 = (1.1 + 2.2);//rr2 = 5.5; // 不能修改return 0;
}

字面常量10原本是不可以被修改的,但是右值引用以后,在特定的位置開辟了變量來存放10,所以就可以被修改了。

表達式或者函數(shù)的返回值,會有一個臨時變量來存放返回值,我們知道這樣的臨時變量具有常性,也是右值。對于這種右值引用,編譯器會修改它的屬性,將常性修改,并且存儲在特定位置。

注意const類型的右值,即便開辟了變量存放該右值也是不可以被修改的,因為被const修飾了。

內置類型的右值被稱為純右值

自定義類型的右值被稱為將亡值。

對于自定義類型的右值,如容器的臨時變量,它確確實實會被銷毀,而不會被存放。

自定義類型的右值才能體現(xiàn)出右值存在的意義,后面會詳細講解。

  • 右值引用是右值的別名,它所指向的右值是不可以被修改的。
  • 但是右值引用本身也是一種類型,并且它的屬性是左值,可以取地址,可以賦值。

3.3?左值引用與右值引用比較

思考:左值引用可以引用右值嗎?

我們要知道,右值引用是C++11才出來的,右值傳參給函數(shù)還是右值,那我們以前寫的函數(shù)都用不了右值傳參了?

template<class T>
void Func(const T& x)
{}

這里去掉const肯定是不能傳參的,為了給右值傳參(當然還有其它原因),所以const的左值引用可以引用右值。總結:普通的左值引用不可以引用右值,const的左值引用可以引用右值:

思考:右值引用可以引用左值嗎?

右值引用不可以引用普通的左值,可以引用move以后的左值:(move這個語法先記住)

左值經過move以后就變成了右值,如:

int main()
{// 左值引用可以引用右值嗎? const的左值引用可以double x = 1.1, y = 2.2;//double& r1 = x + y;const double& r1 = x + y;// 右值引用可以引用左值嗎?可以引用move以后的左值int b = 7;//int&& rr5 = b;int&& rr5 = move(b);return 0;
}

成功編譯:

3.4?右值引用的使用場景

namespace rtx
{class string{public:string(const char* str = ""):_size(strlen(str)), _capacity(_size){_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;//string tmp(s._str);//swap(s);_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}string& operator=(const string& s) // 拷貝賦值{cout << "string& operator=(string s) -- 拷貝賦值(深拷貝)" << endl;string tmp(s);swap(tmp);return *this;}protected:char* _str;size_t _size;size_t _capacity;};
}

先自己實現(xiàn)一個string,只有拷貝構造函數(shù),賦值運算符重載函數(shù),析構函數(shù),以及一個普通的構造函數(shù)。無論是拷貝構造還是賦值運算符重載,都會進行深拷貝,采用現(xiàn)代寫法來實現(xiàn):

namespace rtx
{class string{public:string(const char* str = ""):_size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}const char* c_str() const{return _str;}void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}string(const string& s) // 拷貝構造:_str(nullptr), _size(0), _capacity(0){cout << "string(const string& s) -- 拷貝構造(深拷貝)" << endl;string tmp(s._str);swap(tmp);}	string& operator=(const string& s) // 拷貝賦值{cout << "string& operator=(string s) -- 拷貝賦值(深拷貝)" << endl;string tmp(s);swap(tmp);return *this;}~string(){delete[] _str;_str = nullptr;}protected:char* _str;size_t _size;size_t _capacity;};
}

左值引用的場景:

使用普通傳值調用,存在一次深拷貝:

void Func(rtx::string s)
{}int main()
{rtx::string s("hello world");Func(s);return 0;
}

使用傳拷貝引用時,不存在深拷貝,Func函數(shù)直接使用main函數(shù)中的s1對象:

void Func(rtx::string& s)
{}int main()
{rtx::string s("hello world");Func(s);return 0;
}

函數(shù)返回參數(shù)和上面一樣,傳引用返回有時確實能提高效率,

3.4.1 左值引用的功能和短板

左值引用的功能:
1、做參數(shù)。

a、減少拷貝,提高效率。b、做輸出型參數(shù)。


2、做返回值。

a、減少拷貝,提高效率。b、引用返回,可以修改返回對象(比如: operator[ ])。

但是左值引用做返回值只解決了70%的問題,在類似 to_string 函數(shù)中:

  • 傳值返回時,存在一次深拷貝。
  • rtx::string to_string(int value)

要知道深拷貝的代價是比較大的,深拷貝次數(shù)減少可以很大程度上提高代碼的效率。

  • 傳左值引用返回時,不存在深拷貝。(可以嗎?)
  • rtx::string& to_string(int value)

但是你敢傳引用返回嗎?我們把int value 轉換成string,此時的 string 是一個形參。出了函數(shù)就銷毀了。外面拿到的就是被銷毀了的棧幀。

所以左值引用存在的短板:

前面我們在調用 to_string 函數(shù)的時候,我們把int value 轉換成string,此時的 string 是一個形參。

所以只能傳值返回,此時mian函數(shù)中拿到 to_string 中的 string 對象要進行兩次深拷貝。

?第一次深拷貝,to_string函數(shù)返回時,會將string對象放在一個臨時變量中,此時發(fā)生的深拷貝。函數(shù)返回時,如果是內置類型等幾個字節(jié)的變量,會將函數(shù)中的臨時變量放在寄存器中返回,如果是自定義類型所占空間比較大,就會放在臨時變量中壓棧到上一級棧幀中。

?第二次深拷貝,main函數(shù)中,ret接收函數(shù)返回了的string對象時會再發(fā)生一次深拷貝。

但是編譯器會進行優(yōu)化,將兩次深拷貝優(yōu)化成一次。雖然只有一次,但有些情況代價還是很大的。

C++98是如何解決上面的問題?

那就是輸出型參數(shù):rtx::string to_string(int value)變成rtx::void to_string(int value,string& s)

但是這樣不太符合使用習慣。

  • 有沒有辦法讓它符合使用習慣,并且一次深拷貝都沒有?那就要用到下面的C++11新增的移動構造和移動賦值了

3.4.2?移動構造

此時用右值引用就可以解決這個問題。

右值引用的價值之一:補齊臨時對象不能傳引用返回這個短板。?

前面的深拷貝是拷貝構造產生的:string(const string& s) // 拷貝構造(形參是左值引用)

演示在string類中增加一個移動構造函數(shù):

前面提到過:內置類型的右值被稱為純右值。

自定義類型的右值被稱為將亡值。(這里的傳右值就是將亡值

基于拷貝構造:無論是左值還是右值都老老實實地開空間:

		string(const string& s) // 拷貝構造:_str(nullptr), _size(0), _capacity(0){cout << "string(const string& s) -- 拷貝構造(深拷貝)" << endl;string tmp(s._str);swap(tmp);}	

左值因為還要使用,肯定要開空間的,這里的右值是將亡值,沒用了,所以也不用開空間了,

因為不用開空間了,所以深拷貝也沒了,而是資源轉移(直接swap):

		string(string&& s) // 移動構造:_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 移動構造(資源轉移)" << endl;swap(s);}
  • 移動構造的形參是右值引用。

從to_string中返回的string對象是一個臨時變量,具有常性,也就是我們所說的右值。

  • 用右值來構造string對象時,會自定匹配移動構造函數(shù)。(以前沒有移動構造時,右值傳參會走拷貝構造,因為const 的左值引用可以接收右值,但是這不是最優(yōu)方案,現(xiàn)在寫了移動構造,右值傳參就會走移動構造)

3.4.3?移動賦值

拷貝賦值移動賦值和拷貝構造移動構造類似:

		string& operator=(const string& s) // 拷貝賦值{cout << "string& operator=(string s) -- 拷貝賦值(深拷貝)" << endl;string tmp(s);swap(tmp);return *this;}string& operator=(string&& s) // 移動賦值{cout << "string& operator=(string s) -- 移動賦值(資源移動)" << endl;swap(s);return *this;}

總結:右值引用和左值引用減少拷貝的原理不太一樣。

  • 左值引用是別名,直接在原本的對象上起作用。
  • 右值引用是間接起作用,通過右值引用識別到右值,然后在移動構造和移動賦值中進行資源轉移。

使用移動構造和移動賦值時,被轉移資源的對象必須是個將亡值(像to_string的使用一樣),因為會被銷毀。

C++11的STL標準庫中也提供了移動構造和移動賦值函數(shù)。

3.4.4?插入右值時減少深拷貝

C++11在STL庫容器中的所有插入接口都提供了右值版本,push_back,insert等。

在我們寫的string恢復這兩個接口:

		void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];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';}

然后分別像庫里的 list 插入左值和右值

int main()
{list<rtx::string> lt;rtx::string s1("hello"); // 左值lt.push_back(s1);  // 插入左值cout << "----------------------------------" << endl;lt.push_back(rtx::string("world")); // 插入右值//lt.push_back("world");return 0;
}

如果沒有移動構造那么下面的也是深拷貝了。

4. 完美轉發(fā)

4.1?萬能引用(引用折疊)

寫多個重載函數(shù),根據(jù)實參類型調用不同函數(shù)。

  • ?形參類型分別是左值引用,const左值引用,右值引用,const右值引用:
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); // 此時t變成了左值/const左值
}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;
}

代碼中的perfectForward函數(shù)模板被叫做萬能引用模板,

無論調用該函數(shù)時傳的是什么類型,它都能推演出來:

在函數(shù)模板推演的過程中會發(fā)生引用折疊:模板參數(shù)T&&中的兩個&符號折疊成一個。

當傳入的實參是左值時,就會發(fā)生引用折疊,是右值時就不會發(fā)生引用折疊。

  • 無論傳的實參是什么,都不用改變模板參數(shù)T&&,編譯器都能夠自己推演。
  • 這就是萬能引用,只需要一個模板就可以搞定,不需要分類去寫。

上面萬能模板中,雖然推演出來了各自實參類型,但是由于右值引用本身是左值屬性,所以需要使用move改變屬性后才能調用對應的重載函數(shù)。

有沒有辦法不用move改變左值屬性,讓模板函數(shù)中的t保持它推演出來的類型。答案是有的,完美轉發(fā)就能夠保持形參的屬性不變。

4.2 完美轉發(fā)

完美轉發(fā)同樣是C++11提供的,它也是一個模板:

完美轉發(fā):完美轉發(fā)在傳參的過程中保留對象原生類型屬性。
實參傳遞過來后,推演出的形參是什么類型就保持什么類型繼續(xù)使用。

這里會語法就行:

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(std::forward<T>(t)); // 完美轉發(fā):保持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;
}

此時再使用萬能引用的時候,在函數(shù)模板中調用重載函數(shù)時只需要使用完美轉發(fā)就可以保持推演出來的屬性不變,右值引用仍然是右值,const右值引用也仍然是右值。

需要注意的是:

雖然右值不可以被修改,但是右值引用以后具有了左值屬性,才能被轉移,一旦被const修飾以后就無法轉移了。所以我們在使用右值引用的時候,不要使用const來修飾。

5. 新的類功能

在原來的C++類中,有6大默認成員函數(shù):

1. 構造函數(shù) 2. 析構函數(shù) 3. 拷貝構造函數(shù) 4. 拷貝賦值重載 5. 取地址重載 6. const 取地址重載

最后重要的是前4個,后兩個用處不大。默認成員函數(shù)就是我們不寫編譯器會生成一個默認的,而且完全符號我們使用的需求。

5.1 默認生成的移動構造/賦值

C++11中新增了兩個:移動構造和移動賦值運算符重載,此時C++11一共有8個默認成員函數(shù)了。

這兩個成員函數(shù)在前面已經介紹過了,這里站在默認成員函數(shù)的角度繼續(xù)談談。

滿足下列條件,編譯器會自定生成移動構造函數(shù):

  • 沒有自己顯示定義移動構造函數(shù)
  • 且沒有實現(xiàn)析構函數(shù),拷貝構造函數(shù),拷貝賦值重載中的任何一個。

此時編譯器會自定生成一個默認的移動構造函數(shù)。

  • ?默認生成的移動構造函數(shù),對于內置類型會逐字節(jié)進行拷貝。
  • ?對于自定義類型,如果實現(xiàn)了移動構造就調用移動構造,沒有實現(xiàn)就調用拷貝構造。

滿足下列條件,編譯器會自動生成移動賦值重載函數(shù)

  • 自己沒有顯示定義移動賦值重載函數(shù)。
  • 且且沒有實現(xiàn)析構函數(shù),拷貝構造函數(shù),拷貝賦值重載中的任何一個。

此時編譯器會自動生成一個默認移動賦值函數(shù)。

  • ?對于內置類型會按字節(jié)拷貝。
  • ?對于自定義類型,如果實現(xiàn)了移動賦值就調用移動賦值,如果沒有實現(xiàn)就調用拷貝賦值。

創(chuàng)建一個類,屏蔽掉拷貝構造,拷貝賦值,以及析構函數(shù),成員變量有一個是我們自己實現(xiàn)的string,里面有移動構造和移動賦值。

namespace rtx
{class string{public:string(const char* str = ""):_size(strlen(str)), _capacity(_size){_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), _size(0), _capacity(0){cout << "string(const string& s) -- 拷貝構造(深拷貝)" << endl;string tmp(s._str);swap(tmp);}string(string&& s) // 移動構造:_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 移動構造(資源轉移)" << endl;swap(s);}string& operator=(const string& s) // 拷貝賦值{cout << "string& operator=(string s) -- 拷貝賦值(深拷貝)" << endl;string tmp(s);swap(tmp);return *this;}string& operator=(string&& s) // 移動賦值{cout << "string& operator=(string s) -- 移動賦值(資源移動)" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}protected:char* _str;size_t _size;size_t _capacity;};
}class Person
{
public://Person(const char* name = "", int age = 0)//	:_name(name)//	, _age(age)//{}//Person(const Person& p) // 拷貝構造//	:_name(p._name)//	, _age(p._age)//{}//Person& operator=(const Person& p) // 拷貝賦值//{//	if (this != &p)//	{//		_name = p._name;//		_age = p._age;//	}//	return *this;//}//~Person()//{}protected:rtx::string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);Person s4;s4 = std::move(s2);return 0;
}

此時Person就自動生成了移動構造函數(shù),并且調用了string中的移動構造和移動賦值函數(shù)來構造string對象。

將Person中的拷貝構造,拷貝賦值,析構函數(shù)任意放出一個來。(這里只放出了析構)

使用右值構建string對象時,都會調用string的拷貝構造和拷貝賦值函數(shù)。

  • 編譯器默認生成的移動賦值和移動構造非常類型。
  • 如果符合條件就生成,內置內心按字節(jié)處理,自定義類型調用自定義類型的移動賦值或者移動構造,如果沒有的化就調用它們的拷貝賦值或者拷貝構造。
  • 如果不符合條件,就直接調用自定義類型的拷貝復制或者拷貝構造。

5.2 類里新的關鍵字

強制生成默認函數(shù)的關鍵字default:

這個default并不是switch中的default,而是C++11的新用法。

  • 假設類中的某個默認成員函數(shù)沒有自動生成,但是我們需要它,就可以用default,強制讓編譯器自動生成默認函數(shù)。

5.1里的代碼:將Person中的拷貝構造,拷貝復制,析構函數(shù)都顯示定義,此時就破壞了自動生成移動構造的條件。把Person里的注釋放開,使用default強制生成默認的移動構造函數(shù)

?從結果中可以看到,仍然調用了string中的移動構造函數(shù),而不是調用的拷貝構造(深拷貝)。

  • ?說明Person中仍然生成了默認的移動構造函數(shù)。

禁止生成默認成員函數(shù)的關鍵字delete:

如果能想要限制某些默認函數(shù)的生成,在C++98中,是該函數(shù)設置成private,并且只聲明補丁 已,這樣只要其他人想要調用就會報錯。在C++11中更簡單,只需在該函數(shù)聲明加上=delete即 可,該語法指示編譯器不生成對應函數(shù)的默認版本,稱=delete修飾的函數(shù)為刪除函數(shù)。

C++98不生成默認成員函數(shù)的方法:直接一個分號(要放到保護或者私有里,這里就不放了)

在Person類中不顯示定義拷貝構造函數(shù),拷貝復制函數(shù),析構函數(shù),此時符合自動生成默認移動構造的條件。?聲明移動構造函數(shù),但是沒有定義(要放到保護或者私有里,防止類外實現(xiàn),這里就不放了)。此時在編譯的時候就會報錯,這是C++98中的方式利用鏈接時找不到函數(shù)的定義報錯。C++11就新增delete關鍵字使其在編譯階段就報錯:

  • C++11中,使用delete同樣可以實現(xiàn)不讓自動生成默認成員函數(shù)。

同樣在編譯時報錯了。編譯器會自動生成移動構造函數(shù),但是此時使用了delete,編譯器就會報錯,告訴我們這里生成了移動構造。

這是為了在編譯階段就報錯,而不是運行時再報錯。

以前提到的一道題:

// 要求delete關鍵字實現(xiàn),一個類,只能在堆上創(chuàng)建對象
class HeapOnly
{
public:HeapOnly(){_str = new char[10];}~HeapOnly() = delete;//void Destroy() // 如果要銷毀只能這樣//{//?? ?delete[] _str;//?? ?operator delete(this);//}private:char* _str;//...
};

繼承和多態(tài)中的final與override關鍵字

這兩個關鍵字在繼承和多態(tài)部分詳細講解過,這里不再詳細講解。

final

  • 在繼承中,被final修飾的類叫做最終類,是無法繼承的。
  • 在多態(tài)中,被final修飾的虛函數(shù)是無法進行重寫的。

override

  • 在多態(tài)中,用來檢查虛函數(shù)是否完成了重寫。

本篇完。

C++11中的很多東西雖然讓C++越來越不像C++,比如列表初始化等內容,但是還是有一些非常有用的東西的:比如今天講到的右值引用,和下一篇學的lambda表達式。

下一篇:從C語言到C++_34(C++11_下)可變參數(shù)+ lambda+function+bind+筆試題。

http://www.risenshineclean.com/news/27471.html

相關文章:

  • 群暉套件wordpressseo優(yōu)化教程視頻
  • 便宜做網站灰色行業(yè)推廣平臺網站
  • 網站模板怎么做有沒有免費的廣告平臺
  • 北京做網站的軟文街怎么樣
  • 做電子商務網站需要什么軟件seo標題優(yōu)化褲子關鍵詞
  • 電子商務網站規(guī)劃的原則是什么專門做排名的軟件
  • wp網站源碼百度最新財報
  • 網站制作論文題目網絡營銷師主要做什么
  • 怎樣制作自己的網站網頁游戲推廣平臺
  • 百容千域可以免費做網站嗎上海廣告公司
  • 機械門戶網站建設特點市場營銷推廣策劃
  • 百度收錄網站怎么更改關鍵詞長沙網站優(yōu)化指導
  • 有什么做任務的網站嗎網絡營銷有哪些模式
  • 企業(yè)汽車網站建設關鍵詞優(yōu)化難度查詢
  • 韓國女足還能出線嗎愛站seo綜合查詢
  • 做網站的公司叫什么名字國際新聞最新消息戰(zhàn)爭
  • 專業(yè)網站開發(fā)公司免費發(fā)布推廣的平臺有哪些
  • 企業(yè)網站 .net正規(guī)考證培訓機構
  • 開發(fā)公司總工崗位職責網站優(yōu)化策略分析
  • 無錫微網站開發(fā)方象科技服務案例
  • 作品集用什么網站做模板建站難嗎
  • 網站建設需求怎么寫河北百度推廣客服電話
  • 可以注冊郵箱的網站今天微博熱搜前十名
  • 網站開發(fā)語言怎么看中國國際新聞
  • 網頁版微信二維碼怎么生成seo網站關鍵詞優(yōu)化方法
  • 網站的注冊頁面怎么做黃頁88網
  • 國內外優(yōu)秀設計網站營銷網站搭建
  • 網站開發(fā)怎么做平板電視seo優(yōu)化關鍵詞
  • 網站權限分配代碼知乎小說推廣對接平臺
  • 個人網站備案要求重慶seo全面優(yōu)化