国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

C++中的移動構(gòu)造函數(shù)及move語句示例詳解

2020-05-23 13:45:29
字體:
供稿:網(wǎng)友

前言

本文主要給大家介紹了關(guān)于C++中移動構(gòu)造函數(shù)及move語句的相關(guān)內(nèi)容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。

首先看一個小例子:

#include <iostream>#include <cstring>#include <cstdlib>#include <vector>using namespace std;int main(){ string st = "I love xing"; vector<string> vc ; vc.push_back(move(st)); cout<<vc[0]<<endl; if(!st.empty()) cout<<st<<endl; return 0;}

結(jié)果為:

C++,move構(gòu)造函數(shù),c,移動構(gòu)造函數(shù),move,構(gòu)造函數(shù)

#include <iostream>#include <cstring>#include <cstdlib>#include <vector>using namespace std;int main(){ string st = "I love xing"; vector<string> vc ; vc.push_back(st); cout<<vc[0]<<endl; if(!st.empty()) cout<<st<<endl; return 0;}

結(jié)果為:

C++,move構(gòu)造函數(shù),c,移動構(gòu)造函數(shù),move,構(gòu)造函數(shù)

這兩個小程序唯一的不同是調(diào)用vc.push_back()將字符串插入到容器中去時,第一段代碼使用了move語句,而第二段代碼沒有使用move語句。輸出的結(jié)果差異也很明顯,第一段代碼中,原來的字符串st已經(jīng)為空,而第二段代碼中,原來的字符串st的內(nèi)容沒有變化。

好,記住這兩端代碼的輸出結(jié)果之間的差異。下面我們簡單介紹一下移動構(gòu)造函數(shù)。

在介紹移動構(gòu)造函數(shù)之前,我們先要回顧一下拷貝構(gòu)造函數(shù)。

我們都知道,C++在三種情況下會調(diào)用拷貝構(gòu)造函數(shù)(可能有紕漏),第一種情況是函數(shù)形實結(jié)合時,第二種情況是函數(shù)返回時,函數(shù)棧區(qū)的對象會復制一份到函數(shù)的返回去,第三種情況是用一個對象初始化另一個對象時也會調(diào)用拷貝構(gòu)造函數(shù)。

除了這三種情況下會調(diào)用拷貝構(gòu)造函數(shù),另外如果將一個對象賦值給另一個對象,這個時候回調(diào)用重載的賦值運算符函數(shù)。

無論是拷貝構(gòu)造函數(shù),還是重載的賦值運算符函數(shù),我記得當時在上C++課的時候,老師再三強調(diào),一定要注意指針的淺層復制問題。

這里在簡單回憶一下拷貝構(gòu)造函數(shù)中的淺層復制問題

首先看一個淺層復制的代碼

#include <iostream>#include <cstring>#include <cstdlib>#include <vector>using namespace std;class Str{ public: char *value; Str(char s[]) { cout<<"調(diào)用構(gòu)造函數(shù)..."<<endl; int len = strlen(s); value = new char[len + 1]; memset(value,0,len + 1); strcpy(value,s); } Str(Str &v) { cout<<"調(diào)用拷貝構(gòu)造函數(shù)..."<<endl; this->value = v.value; } ~Str() { cout<<"調(diào)用析構(gòu)函數(shù)..."<<endl; if(value != NULL)  delete[] value; }};int main(){ char s[] = "I love BIT"; Str *a = new Str(s); Str *b = new Str(*a); delete a; cout<<"b對象中的字符串為:"<<b->value<<endl; delete b; return 0;}

輸出結(jié)果為:

C++,move構(gòu)造函數(shù),c,移動構(gòu)造函數(shù),move,構(gòu)造函數(shù)

首先結(jié)果并不符合預期,我們希望b對象中的字符串也是I love BIT但是輸出為空,這是因為b->value和a->value指向了同一片內(nèi)存區(qū)域,當delete a的時候,該內(nèi)存區(qū)域已經(jīng)被收回,所以再用b->value訪問那塊內(nèi)存實際上是不合適的,而且,雖然我運行時程序沒有崩潰,但是程序存在崩潰的風險呀,因為當delete b的時候,那塊內(nèi)存區(qū)域又被釋放了一次,兩次釋放同一塊內(nèi)存,相當危險呀。

我們用valgrind檢查一下,發(fā)現(xiàn),相當多的內(nèi)存錯誤呀!

C++,move構(gòu)造函數(shù),c,移動構(gòu)造函數(shù),move,構(gòu)造函數(shù)

其中就有一個Invalid free 也就是刪除b的時候調(diào)用析構(gòu)函數(shù),對已經(jīng)釋放掉對空間又釋放了一次。

那么深層復制應(yīng)該怎樣寫呢?

代碼如下:

#include <iostream>#include <cstring>#include <cstdlib>#include <vector>using namespace std;class Str{ public: char *value; Str(char s[]) { cout<<"調(diào)用構(gòu)造函數(shù)..."<<endl; int len = strlen(s); value = new char[len + 1]; memset(value,0,len + 1); strcpy(value,s); } Str(Str &v) { cout<<"調(diào)用拷貝構(gòu)造函數(shù)..."<<endl; int len = strlen(v.value); value = new char[len + 1]; memset(value,0,len + 1); strcpy(value,v.value); } ~Str() { cout<<"調(diào)用析構(gòu)函數(shù)..."<<endl; if(value != NULL) {  delete[] value;  value = NULL; } }};int main(){ char s[] = "I love BIT"; Str *a = new Str(s); Str *b = new Str(*a); delete a; cout<<"b對象中的字符串為:"<<b->value<<endl; delete b; return 0;}

結(jié)果為:

C++,move構(gòu)造函數(shù),c,移動構(gòu)造函數(shù),move,構(gòu)造函數(shù)

這次達到了我們預想的效果,而且,用valgrind檢測一下,發(fā)現(xiàn),沒有內(nèi)存錯誤!

C++,move構(gòu)造函數(shù),c,移動構(gòu)造函數(shù),move,構(gòu)造函數(shù)

所以,寫拷貝構(gòu)造函數(shù)的時候,切記要注意指針的淺層復制問題呀! 

好的,回顧了一下拷貝構(gòu)造函數(shù),下面回到移動構(gòu)造函數(shù)上來。

有時候我們會遇到這樣一種情況,我們用對象a初始化對象b,后對象a我們就不在使用了,但是對象a的空間還在呀(在析構(gòu)之前),既然拷貝構(gòu)造函數(shù),實際上就是把a對象的內(nèi)容復制一份到b中,那么為什么我們不能直接使用a的空間呢?這樣就避免了新的空間的分配,大大降低了構(gòu)造的成本。這就是移動構(gòu)造函數(shù)設(shè)計的初衷。

下面這個圖,很好地說明了拷貝構(gòu)造函數(shù)和移動構(gòu)造函數(shù)的區(qū)別。

C++,move構(gòu)造函數(shù),c,移動構(gòu)造函數(shù),move,構(gòu)造函數(shù)

看明白了嗎?

通俗一點的解釋就是,拷貝構(gòu)造函數(shù)中,對于指針,我們一定要采用深層復制,而移動構(gòu)造函數(shù)中,對于指針,我們采用淺層復制

但是上面提到,指針的淺層復制是非常危險的呀。沒錯,確實很危險,而且通過上面的例子,我們也可以看出,淺層復制之所以危險,是因為兩個指針共同指向一片內(nèi)存空間,若第一個指針將其釋放,另一個指針的指向就不合法了。所以我們只要避免第一個指針釋放空間就可以了。避免的方法就是將第一個指針(比如a->value)置為NULL,這樣在調(diào)用析構(gòu)函數(shù)的時候,由于有判斷是否為NULL的語句,所以析構(gòu)a的時候并不會回收a->value指向的空間(同時也是b->value指向的空間)

所以我們可以把上面的拷貝構(gòu)造函數(shù)的代碼修改一下:

#include <iostream>#include <cstring>#include <cstdlib>#include <vector>using namespace std;class Str{ public: char *value; Str(char s[]) { cout<<"調(diào)用構(gòu)造函數(shù)..."<<endl; int len = strlen(s); value = new char[len + 1]; memset(value,0,len + 1); strcpy(value,s); } Str(Str &v) { cout<<"調(diào)用拷貝構(gòu)造函數(shù)..."<<endl; this->value = v.value; v.value = NULL; } ~Str() { cout<<"調(diào)用析構(gòu)函數(shù)..."<<endl; if(value != NULL)  delete[] value; }};int main(){ char s[] = "I love BIT"; Str *a = new Str(s); Str *b = new Str(*a); delete a; cout<<"b對象中的字符串為:"<<b->value<<endl; delete b; return 0;}

結(jié)果為:

 C++,move構(gòu)造函數(shù),c,移動構(gòu)造函數(shù),move,構(gòu)造函數(shù)

修改后的拷貝構(gòu)造函數(shù),采用了淺層復制,但是結(jié)果仍能夠達到我們想要的效果,關(guān)鍵在于在拷貝構(gòu)造函數(shù)中,最后我們將v.value置為了NULL,這樣在析構(gòu)a的時候,就不會回收a->value指向的內(nèi)存空間

這樣用a初始化b的過程中,實際上我們就減少了開辟內(nèi)存,構(gòu)造成本就降低了。

但要注意,我們這樣使用有一個前提是:用a初始化b后,a我們就不需要了,最好是初始化完成后就將a析構(gòu)。如果說,我們用a初始化了b后,仍要對a進行操作,用這種淺層復制的方法就不合適了。

所以C++引入了移動構(gòu)造函數(shù),專門處理這種,用a初始化b后,就將a析構(gòu)的情況。

*************************************************************

**移動構(gòu)造函數(shù)的參數(shù)和拷貝構(gòu)造函數(shù)不同,拷貝構(gòu)造函數(shù)的參數(shù)是一個左值引用,但是移動構(gòu)造函數(shù)的初值是一個右值引用。(關(guān)于右值引用大家可以看我之前的文章,或者查找其他資料)。這意味著,移動構(gòu)造函數(shù)的參數(shù)是一個右值或者將亡值的引用。也就是說,只用用一個右值,或者將亡值初始化另一個對象的時候,才會調(diào)用移動構(gòu)造函數(shù)。而那個move語句,就是將一個左值變成一個將亡值。

移動構(gòu)造函數(shù)應(yīng)用最多的地方就是STL中

給出一個代碼,大家自行驗證使用move和不適用move的區(qū)別吧

#include <iostream>#include <cstring>#include <cstdlib>#include <vector>using namespace std;class Str{ public: char *str; Str(char value[]) {  cout<<"普通構(gòu)造函數(shù)..."<<endl;  str = NULL;  int len = strlen(value);  str = (char *)malloc(len + 1);  memset(str,0,len + 1);  strcpy(str,value); } Str(const Str &s) {  cout<<"拷貝構(gòu)造函數(shù)..."<<endl;  str = NULL;  int len = strlen(s.str);  str = (char *)malloc(len + 1);  memset(str,0,len + 1);  strcpy(str,s.str); } Str(Str &&s) {  cout<<"移動構(gòu)造函數(shù)..."<<endl;  str = NULL;  str = s.str;  s.str = NULL; } ~Str() {  cout<<"析構(gòu)函數(shù)"<<endl;  if(str != NULL)  {  free(str);  str = NULL;  } }};int main(){ char value[] = "I love zx"; Str s(value); vector<Str> vs; //vs.push_back(move(s)); vs.push_back(s); cout<<vs[0].str<<endl; if(s.str != NULL) cout<<s.str<<endl; return 0;}

總結(jié)

以上就是這篇文章的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網(wǎng)的支持。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 和平县| 西吉县| 沭阳县| 玉门市| 子洲县| 富裕县| 汝南县| 芦溪县| 佛学| 鄂托克旗| 长宁区| 珠海市| 石棉县| 施秉县| 大悟县| 永安市| 鄂托克旗| 札达县| 通许县| 哈尔滨市| 浦东新区| 绵竹市| 房山区| 合山市| 林西县| 温州市| 方城县| 奈曼旗| 米脂县| 武隆县| 无棣县| 泰顺县| 镇安县| 南丰县| 突泉县| 元谋县| 广灵县| 乐清市| 汪清县| 广灵县| 延川县|