<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
我們知道,每個變數都有型別,或整形或字元型等來進行了分類,不僅如此,C++表示式(帶有運算元的操作符、字面量、變數名等)在型別的屬性上,還有一種屬性,即值類別(value category)。且每個表示式只屬於三種基本值尖別中的一種:左值(lvalue),右值(rvalue),將亡值(xvalue),每個值類別都與某種參照型別對應。
其中,左值和將亡值成為泛左值(generalized value,gvalue),純右值和將亡值合稱為右值(right value,rvalue)。
一般我們講,左值就是可以取地址的,具有名字的,比如 int a; a是變數的名字,&a是變數的地址,a就是左值。那麼右值呢,自然就是不可以取地址的,比如int b=10; 而這個10就是一個右值,在記憶體中不會分配有地址,自然也不能取地址。
將亡值,則是指在呼叫某個函數退出返回時,如果函數有返回值,那麼就會有將亡值的存在,為什麼稱之為將亡值,就是說這個值在函數作用域建立,但由於函數返回結束,區域性變數都會銷燬,故會產生一個將亡值來接收這個值,完成賦值的任務。
從上圖也可以看出,將亡值既可能轉為左值,也可能成為右值,那麼關鍵就在於要看是否具有名字了。
下面看這樣一段程式:
#include<iostream> #include<type_traits> using namespace std; class MyString { private: char* str; // heap; public: MyString(const char* p = nullptr) :str(nullptr) { if (p != nullptr) { int n = strlen(p) + 1; str = new char[n]; strcpy_s(str, n, p); } cout << "Create MyString: " << this << endl; } MyString(const MyString& st) { if(st.str!=NULL) str = st.str; cout << "Copy Create MyString: " << this << endl; } MyString& operator=(const MyString& st) { if (st.str != NULL) str = st.str; cout << this << " operator=(const MyString &): " << &st << endl; return *this; } ~MyString() { delete[]str; str = nullptr; cout << "Destroy MyString : " << this << endl; } void PrintString() const { if (str != nullptr) { cout << str << endl; } } }; int main() { MyString *a=new MyString("lisa"); MyString *b = a; delete b; a->PrintString(); return 0; }
MyString型別成員有指標變數,且採用淺拷貝方式。當程式執行時,可以看到,兩個指標指向了同一個地址,此時,若釋放了b指標,再以a指標存取指標成員,就會出現問題。
還有,當函數以值型別返回,構造臨時物件,若有指標變數,且採用淺拷貝,就會出現多次解構的問題,導致程式崩潰。
當我們將程式都改為深拷貝時,深拷貝又會導致,程式多次騷擾對空間,此時就提出了move語意。
std::move
std::move其實並沒有移動任何東西,它唯一的功能是將一個左值強制轉化為右值參照,繼而可以通過右值參照使用該值,以用於移動語意。從實現上講,move基本等同於一個型別轉換。
值得注意的是,通過move轉化成右值後,被轉化的左值的生命週期並沒有隨著左右值的轉化而改變。但通常情況下,我們需要轉換成右值參照的還是一個確定生命期即將結束的物件。
在c++11中增加了右值參照的概念,即對右值的參照,通過右值參照,可以延長右值的生命期。我們都知道左值參照是變數值的別名,那麼右值參照則是不具名變數的別名。
右值參照是不能繫結到任何左值的,但有個例外,常數左值是一個萬能參照,可以參照任何值,包括右值參照。
class MyString { private: char* str; // heap; public: MyString(const char* p = nullptr) :str(nullptr) { if (p != nullptr) { int n = strlen(p) + 1; str = new char[n]; strcpy_s(str, n, p); } cout << "Create MyString: " << this << endl; } MyString(const MyString& st) { if (st.str != nullptr) { int n = strlen(st.str) + 1; str = new char[n]; strcpy_s(str, n, st.str); } cout << "Copy Create MyString: " << this << endl; } MyString& operator=(const MyString& st) { if (this != &st && str != st.str) { delete[]str; if (st.str != nullptr) { int n = strlen(st.str) + 1; str = new char[n]; strcpy_s(str, n, st.str); } } cout << this << " operator=(const MyString &): " << &st << endl; return *this; } MyString(MyString&& st) { str = st.str; st.str = nullptr; cout << "Move Copy Create MyString" << this << endl; } MyString& operator=(MyString&& st) { if (this == &st) return *this; if (this->str == st.str) { st.str = nullptr; return *this; } delete[]str; str = st.str; st.str = nullptr; cout << "Move operator=(MyString &&)" << endl; return *this; } ~MyString() { delete[]str; str = nullptr; cout << "Destroy MyString : " << this << endl; } void PrintString() const { if (str != nullptr) { cout << str << endl; } } }; int main() { const MyString stra("hello"); MyString strb; strb = std::move(stra);//呼叫普通的賦值方法 strb.PrintString(); return 0; }
這裡的move還是呼叫普通的賦值函數,並未做到真正的資源轉移,但是若寫成如下結構:
int main() { const MyString stra("hello"); MyString strb; //strb = std::move(stra);//呼叫普通的賦值方法 strb = (MyString&&)stra; strb.PrintString(); return 0; }
通過右值參照,可以延長右值的生命期。從而,有了右值參照出現,這個時候配合移動構造與移動賦值,就可以完成資源轉移了。
然後,我們再看一個例子:
MyString& fun() { MyString st=("newdata"); return st;//xvalue } int main() { MyString("zhangsan").PrintString(); const MyString& a = fun(); a.PrintString(); MyString& b = fun(); b.PrintString(); return 0; }
在程式執行時,會發現程式崩潰了,原因是:
函數中返回區域性物件的參照,因為函數呼叫結束會銷燬區域性物件,而參照則就成為了非法的存取。因為不要在函數中返回區域性物件的參照。
若我們將fun()函數的返回改為右值參照呢?
MyString&& fun() { return MyString("newdata"); } int main() { MyString("zhangsan").PrintString(); const MyString& a = fun();//x a.PrintString(); //MyString& b = fun(); //b.PrintString(); MyString&& c = fun();//x c.PrintString(); MyString&& d = c;//error return 0; }
將亡值回去的時候,就得看看有沒有具名,一旦具名就是左值了,否則是右值
可以發現,右值參照是不具名的,但是右值參照本身卻是個左值,經過右值參照b接收後,就已經變成了左值,具有了名字。
到此這篇關於C++模板程式設計特性之移動語意的文章就介紹到這了,更多相關C++移動語意內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45