c++之移動(dòng)構(gòu)造函數(shù)或者移動(dòng)賦值運(yùn)算符的作用詳解
原理介紹
前面的文章中有的涉及到了移動(dòng)構(gòu)造函數(shù)或者移動(dòng)賦值運(yùn)算符,對(duì)于它們的形式有了一定的了解,但是對(duì)他們的核心作用以及為什么要引入這兩個(gè)東東,很多朋友還是一知半解。本文就是來解決這個(gè)問題的。
要理解這個(gè)問題繞不開右值引用,c++11之前的 版本中只有拷貝構(gòu)造函數(shù),而拷貝構(gòu)造函數(shù)是一般會(huì)深拷貝,即會(huì)創(chuàng)建兩個(gè)完全一樣的對(duì)象,包括指針指向的區(qū)域都會(huì)進(jìn)行重新申請(qǐng)內(nèi)存和拷貝。這種情況下如果對(duì)象是一個(gè)大數(shù)組或占用資源多的對(duì)象,就會(huì)產(chǎn)生很大的內(nèi)存拷貝開銷。那么如何解決這個(gè)問題呢?
c+11版本引入了右值引用、移動(dòng)語義來解決這個(gè)問題。這里先說結(jié)論:通過移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符產(chǎn)生的對(duì)象與元對(duì)象會(huì)產(chǎn)生一個(gè)資源的轉(zhuǎn)移。舉個(gè)形象的例子:小明手里有個(gè)籃球,小王手里沒有籃球,以前的拷貝構(gòu)造函數(shù)或者拷貝賦值運(yùn)算符重載的結(jié)果是小王會(huì)按照小明手里的籃球重新買一個(gè);而移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符重載實(shí)現(xiàn)則更像是小王把小明手里的籃球放到自己手里,而小明手里沒有了籃球。
從上面的例子可以看出移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符的作用就是通過資源管理權(quán)轉(zhuǎn)移的方式實(shí)現(xiàn)對(duì)象的構(gòu)造,使得可以減少內(nèi)存拷貝的開銷。
移動(dòng)構(gòu)造函數(shù)的的參數(shù)一定是一個(gè)右值引用。
下面是一個(gè)給出一個(gè)移動(dòng)構(gòu)造函數(shù)的典型的例子:
class MyClass
{
std::string str; //一個(gè)類成員
int* ptr; //一個(gè)指針成員
public:
A(){}
A(MyClass && a)::str(std::move(a.str))
{
}
A operator=(MyClass&& a) //移動(dòng)賦值運(yùn)算符重載
{
if(this != &a)
{
str = std:move(a.str); //這里因?yàn)閟tr::string類已經(jīng)實(shí)現(xiàn)了移動(dòng)構(gòu)造賦值運(yùn)算符,std::string作為一個(gè)類已經(jīng)支持了這個(gè),所以可以這樣做,如果是
ptr = a.ptr; //需要自己去切換指針?biāo)赶虻膮^(qū)域
a.ptr = nullptr;
}
}
}注意事項(xiàng)
- 一個(gè)類的移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符的實(shí)現(xiàn)是由類自己實(shí)現(xiàn)的
- 如果要使用移動(dòng)構(gòu)造函數(shù)或者移動(dòng)賦值運(yùn)算符,那么用戶必須顯式的定義移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符的重載
- 一些c++標(biāo)準(zhǔn)庫中提供的類很多都已經(jīng)實(shí)現(xiàn)了移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符的重載,例如std::string、stl容器類、智能指針(std::shared_ptr<T>、std::unique_ptr<T>)等
- 用戶自己定義類在有需要的情況下需要在自定義的類中實(shí)現(xiàn)移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符重載。
- 移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符重載實(shí)現(xiàn)基于右值引用,一定是以右值引用作為參數(shù)的
思考
移動(dòng)構(gòu)造函數(shù)本質(zhì)是資源管理權(quán)的轉(zhuǎn)移,那如果跟std::shared_ptr<T>會(huì)發(fā)生什么?
- 請(qǐng)看下面的代碼:
class MyClass
{
public:
A(){
std::cout<<"A的構(gòu)造函數(shù)"<<std::endl;
}
~A(){
std::cout<<"A的析構(gòu)函數(shù)"<<std::endl;
}
}
class Container
{
private:
std::shared_ptr<MyClass> ptr;
public:
Container(Container&& container):ptr(std::move(container.ptr))
{
std::cout<<"移動(dòng)構(gòu)造函數(shù)"<<std::endl;
}
Container operator=(Container&& container)
{
if(this != &container) //這里不能少
{
ptr = std::move(container.ptr); //這里也可以,因?yàn)閟td::shared_ptr內(nèi)部實(shí)習(xí)拿了移動(dòng)賦值運(yùn)算符
}
}
}shared_ptr 的移動(dòng)操作:
- 轉(zhuǎn)移資源所有權(quán)
- 源指針置空
- 保持引用計(jì)數(shù)不變
- 是線程安全和異常安全的
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C++實(shí)現(xiàn)Date類各種運(yùn)算符重載的示例代碼
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)Date類各種運(yùn)算符重載的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02
c++實(shí)現(xiàn)圖像像素計(jì)算的示例詳解
我們知道每張圖像都能夠用矩陣來表示,矩陣中每個(gè)元素的值表示了圖像中每個(gè)像素值,像素值的大小就對(duì)應(yīng)著圖像的亮暗,本文主要來和大家介紹一下C++進(jìn)行圖像像素計(jì)算的相關(guān)知識(shí),感興趣的可以了解下2023-12-12
關(guān)于C++中構(gòu)造函數(shù)初始化成員列表的總結(jié)
下面小編就為大家?guī)硪黄P(guān)于C++中構(gòu)造函數(shù)初始化成員列表的總結(jié)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12

