C++ Primer:第13章 拷貝控制


第13章 拷貝控制

  1. 拷貝控制操作:拷貝構(gòu)造函數(shù)错览、拷貝賦值運(yùn)算符理澎、移動構(gòu)造函數(shù)逞力、移動賦值運(yùn)算符、析構(gòu)函數(shù)糠爬。
  2. 當(dāng)類中沒有聲明構(gòu)造函數(shù)時寇荧,編譯器會在其需要時生成合成默認(rèn)構(gòu)造函數(shù)。當(dāng)類中沒有定義拷貝構(gòu)造函數(shù)時执隧,編譯器生成合成拷貝構(gòu)造函數(shù)揩抡。合成拷貝賦值運(yùn)算符合成析構(gòu)函數(shù)與合成拷貝構(gòu)造函數(shù)類似镀琉。當(dāng)類中沒有自定義拷貝控制成員峦嗤,且每個非static數(shù)據(jù)成員都可以移動時,編譯器才會合成移動構(gòu)造函數(shù)或移動賦值運(yùn)算符屋摔。
  3. 若一個類需要析構(gòu)函數(shù)烁设,則幾乎肯定需要拷貝構(gòu)造函數(shù)拷貝賦值運(yùn)算符钓试;若一個類需要拷貝構(gòu)造函數(shù)装黑,則幾乎肯定需要拷貝賦值運(yùn)算符;若一個類需要拷貝賦值運(yùn)算符弓熏,則幾乎肯定需要拷貝構(gòu)造函數(shù)恋谭。若一個類定義任意一個拷貝控制,則應(yīng)該定義所有的5個拷貝控制操作挽鞠。

13.1 拷貝疚颊、賦值與銷毀

1. 拷貝構(gòu)造函數(shù)

  1. 拷貝構(gòu)造函數(shù)的第一個參數(shù)是自身類型的引用,且任意額外參數(shù)都有默認(rèn)值滞谢〈。拷貝構(gòu)造函數(shù)通常不是explicit,第一個參數(shù)幾乎總是const狮杨。
  2. 直接初始化要求編譯器通過函數(shù)匹配選擇構(gòu)造函數(shù)母截,拷貝初始化要求編譯器將右側(cè)運(yùn)算對象拷貝到左側(cè)運(yùn)算對象,必要時可進(jìn)行類型轉(zhuǎn)換橄教。
  3. 使用拷貝初始化的情況:使用=定義變量清寇;將實(shí)參傳遞給非引用形參;返回類型為非引用類型的函數(shù)返回對象护蝶;花括號列表初始化數(shù)組元素或聚合類成員华烟;某些類類型會對其分配的對象進(jìn)行拷貝初始化,如vectorinsertpush進(jìn)行拷貝初始化持灰,emplace進(jìn)行直接初始化盔夜。
  4. 拷貝構(gòu)造函數(shù)的第一個參數(shù)必須是引用類型,因?yàn)楹瘮?shù)調(diào)用過程中,非引用類型的形參通過拷貝構(gòu)造函數(shù)進(jìn)行拷貝初始化喂链。若第一個參數(shù)不是引用類型返十,函數(shù)調(diào)用時非引用類型的形參使用拷貝構(gòu)造函數(shù)初始化,而拷貝構(gòu)造函數(shù)的第一個參數(shù)是非引用類型椭微,第一個參數(shù)又需要調(diào)用拷貝構(gòu)造函數(shù)洞坑,如此會無限循環(huán)。
  5. 雖然編譯器可以略過拷貝/移動構(gòu)造函數(shù)蝇率,但依然要求拷貝/移動構(gòu)造函數(shù)必須存在且可訪問迟杂。
class Foo{
public:
    Foo(); // 默認(rèn)構(gòu)造函數(shù)
    Foo(const Foo&); // 拷貝構(gòu)造函數(shù)
}

string s = "1"; // 拷貝初始化,等價于string temp("1"); string s = temp; //使用拷貝構(gòu)造函數(shù)
string s("1"); // "1":const char *本慕,略過拷貝構(gòu)造函數(shù)

2. 拷貝賦值運(yùn)算符

  1. 賦值運(yùn)算符通常返回一個指向其左側(cè)運(yùn)算對象的引用排拷。標(biāo)準(zhǔn)庫通常要求保存在容器中的類型要具有賦值運(yùn)算符,且其返回值是左側(cè)運(yùn)算對象的引用间狂。
  2. 大多數(shù)賦值運(yùn)算符會結(jié)合析構(gòu)函數(shù)和拷貝構(gòu)造函數(shù)的工作攻泼。編寫賦值運(yùn)算符時需注意自賦值情況,最好是在銷毀左側(cè)運(yùn)算對象資源之前拷貝右側(cè)運(yùn)算對象鉴象。

3. 析構(gòu)函數(shù)

  1. 在構(gòu)造函數(shù)中,成員初始化在函數(shù)體之前完成何鸡,且按照它們在類中出現(xiàn)的順序初始化纺弊。在析構(gòu)函數(shù)中,先執(zhí)行函數(shù)體骡男,再按照初始化順序的逆序銷毀成員淆游。
  2. 若成員是內(nèi)置指針類型,析構(gòu)函數(shù)不會自動delete其所指的對象隔盛。若成員是智能指針犹菱,因智能指針是類類型,故會執(zhí)行類成員自己的析構(gòu)函數(shù)實(shí)現(xiàn)自動銷毀吮炕。
  3. 調(diào)用析構(gòu)函數(shù)的情況:變量離開作用域時被銷毀腊脱;當(dāng)一個對象被銷毀時,其成員被銷毀龙亲;標(biāo)準(zhǔn)庫容器或數(shù)組被銷毀時陕凹,其元素被銷毀;對于動態(tài)分配的對象鳄炉,當(dāng)delete指向該對象的指針時被銷毀杜耙;對于臨時對象,當(dāng)創(chuàng)建它的完整表達(dá)式結(jié)束時被銷毀拂盯。
  4. 當(dāng)指向一個對象的引用或指針離開作用域時不會執(zhí)行析構(gòu)函數(shù)佑女。

4. =default=delete

  1. 使用=default可顯式要求編譯器生成合成版本的拷貝控制成員。在類內(nèi)使用=default,合成的函數(shù)是內(nèi)聯(lián)的团驱,在類外使用=default摸吠,合成的函數(shù)就不是內(nèi)聯(lián)的。只能對具有合成版本的成員函數(shù)(默認(rèn)構(gòu)造函數(shù)店茶,拷貝控制成員)使用=default蜕便。
  2. 使用=delete表示不能以任意方式調(diào)用該成員函數(shù)。=delete必須出現(xiàn)在函數(shù)第一次聲明的時候贩幻〗蜗伲可對任意函數(shù)使用=delete,不局限于默認(rèn)構(gòu)造函數(shù)和拷貝控制成員丛楚。
  3. 最好不要對析構(gòu)函數(shù)使用=delete族壳。對于析構(gòu)函數(shù)已刪除的類型,不能定義該類型的變量趣些,可動態(tài)分配但不能釋放該類型的對象仿荆。
  4. 將拷貝控制成員聲明為private但不定義,可阻止用戶代碼坏平、友元函數(shù)拢操、成員函數(shù)進(jìn)行拷貝控制。
=delete 原因
合成默認(rèn)構(gòu)造函數(shù) 1舶替、類成員的析構(gòu)函數(shù)是刪除(=delete)或不可訪問(private)令境。
2、類中含有引用成員顾瞪,該成員沒有類內(nèi)初始化器舔庶。
3、類中含有const成員陈醒,該成員沒有類內(nèi)初始化器惕橙,且其類型未顯式定義默認(rèn)構(gòu)造函數(shù)。
合成拷貝構(gòu)造函數(shù) 1钉跷、類成員的拷貝構(gòu)造函數(shù)是刪除或不可訪問弥鹦。
2、類成員的析構(gòu)函數(shù)是刪除或不可訪問尘应。
合成拷貝賦值運(yùn)算符 1惶凝、類成員的拷貝賦值運(yùn)算符是刪除或不可訪問。
2犬钢、類中含有const成員或引用成員苍鲜。
合成析構(gòu)函數(shù) 1、類成員的析構(gòu)函數(shù)是刪除或不可訪問玷犹。

13.2 拷貝控制和資源管理

  1. 可定義拷貝操作使類的行為看起來像一個值或一個指針混滔。類的行為像一個值,意味著類有自己的狀態(tài),副本與原對象無關(guān)坯屿,修改副本不會改變原對象油湖。類的行為像一個指針,則類共享狀態(tài)领跛,副本與原對象使用相同底層數(shù)據(jù)乏德,修改副本會改變原對象。
  2. 指針成員的拷貝決定類的行為像值或像指針吠昭。
  3. 令類的行為像指針喊括,最好是使用share_ptr管理類中的資源。若想直接管理資源矢棚,則需使用引用計(jì)數(shù)郑什。可將引用計(jì)數(shù)保存在動態(tài)內(nèi)存中蒲肋。

13.3 交換操作

  1. 當(dāng)作用域有using std::swap蘑拯,若存在類型特定的swap版本,swap調(diào)用會與之匹配兜粘,若不存在類型特定版本申窘,則會使用std::swap
  2. 對于行為類值的類,賦值運(yùn)算符通過拷貝并交換技術(shù)(形參是非引用類型孔轴,swap定義賦值運(yùn)算符)可自動處理自賦值情況且天然就是異常安全的偶洋。

13.4 拷貝控制示例

  1. 當(dāng)類需要分配資源、簿記工作(類似于郵件處理應(yīng)用中的MessageFolder)等操作時距糖,通常需要拷貝控制。

13.5 動態(tài)內(nèi)存管理類

  1. 當(dāng)類需要在運(yùn)行時分配可變大小的內(nèi)存空間時牵寺,通常使用標(biāo)準(zhǔn)庫容器保存其數(shù)據(jù)悍引。
  2. 若類需要自己進(jìn)行內(nèi)存分配,則必須定義自己的拷貝控制成員來管理所分配的內(nèi)存帽氓。

13.6 對象移動

  1. 移動而非拷貝對象的情況:對象拷貝后就立即被銷毀趣斤;IOunique_ptr等類中包含不能被共享的資源(如IO緩沖黎休、指針)浓领。
  2. 標(biāo)準(zhǔn)庫容器、stringshared_ptr類同時支持拷貝和移動势腮,IOunique_ptr類只支持移動联贩。

1. 左值引用

  1. 右值引用&&即必須綁定到右值的引用。右值引用只能綁定到一個將要銷毀的對象捎拯。
  2. 左值和右值是表達(dá)式的屬性泪幌,左值表達(dá)式表示一個對象的身份,右值表達(dá)式表示對象的值。左值有持久的狀態(tài)祸泪,右值只能是字面常量或表達(dá)式求值過程中創(chuàng)建的臨時對象吗浩。
  3. 非const左值引用可以綁定賦值、下標(biāo)没隘、解引用懂扼、前置遞增/遞減、返回左值引用的函數(shù)右蒲;const左值引用右值引用可以綁定算術(shù)阀湿、關(guān)系、位品嚣、后置遞增/遞減炕倘、要求轉(zhuǎn)換的表達(dá)式、字面常量翰撑、返回右值的表達(dá)式罩旋。
  4. std::move可將左值轉(zhuǎn)換為對應(yīng)的右值引用類型搔弄。移后源對象(使用std::move后的對象)可以被銷毀或賦值蚊逢,但不能使用其值。
int i = 42;
int &r1 = i;
const int &r2 = 42;
int && r3 = 42;
int &&r4 = r3; // 錯誤郁季,r3是變量逝撬,變量是左值
int &&r5 = std::move(i);

2. 移動構(gòu)造函數(shù)和移動賦值運(yùn)算符

  1. 移動構(gòu)造函數(shù)第一個參數(shù)是非const的右值引用浴骂,任何額外參數(shù)必須有默認(rèn)實(shí)參。移動構(gòu)造函數(shù)需完成資源移動宪潮,保證移后源對象可被銷毀和賦值溯警。
  2. 不拋出異常的移動構(gòu)造函數(shù)和移動賦值函數(shù)必須標(biāo)記noexcept
  3. 當(dāng)類中沒有自定義拷貝控制成員狡相,且每個非static數(shù)據(jù)成員都可以移動時梯轻,編譯器才會合成移動構(gòu)造函數(shù)或移動賦值運(yùn)算符。
  4. 定義了一個移動構(gòu)造函數(shù)或移動賦值運(yùn)算符的類必須定義自己的拷貝操作尽棕,否則合成拷貝構(gòu)造函數(shù)和合成拷貝賦值運(yùn)算符會被定義為刪除的喳挑。
  5. 若類中拷貝操作與移動操作同時存在,則進(jìn)行函數(shù)匹配滔悉,實(shí)參是左值的函數(shù)會使用拷貝操作伊诵,實(shí)參是右值的函數(shù)會使用移動操作。若類中只有拷貝操作且實(shí)參是右值回官,則會調(diào)用拷貝操作曹宴。
  6. make_move_iterator可將普通迭代器轉(zhuǎn)換為移動迭代器。
  7. 不要隨便使用移動操作孙乖。移后源對象具有不確定的狀態(tài)浙炼,對其調(diào)用std::move很危險份氧。當(dāng)我們調(diào)用move時,必須絕對確認(rèn)移后源對象沒有其它用戶弯屈。
=delete 原因
移動構(gòu)造函數(shù) 1蜗帜、類成員定義自己的拷貝構(gòu)造函數(shù)且未定義移動構(gòu)造函數(shù)
2、類成員未定義自己的拷貝構(gòu)造函數(shù)且編譯器不能合成移動構(gòu)造函數(shù)
3资厉、類成員的移動構(gòu)造函數(shù)被定義為刪除的或不可訪問的
4厅缺、類的析構(gòu)函數(shù)被定義為刪除的或不可訪問的
移動賦值運(yùn)算符 1、類成員定義自己的拷貝賦值運(yùn)算符且未定義移動賦值運(yùn)算符
2宴偿、類成員未定義自己的拷貝賦值運(yùn)算符且編譯器不能合成移動賦值運(yùn)算符
3湘捎、類成員的移動賦值運(yùn)算符被定義為刪除的或不可訪問的
4、類成員是const或引用

3. 右值引用和成員函數(shù)

  1. 除構(gòu)造函數(shù)和賦值運(yùn)算符外窄刘,其它成員函數(shù)也可同時提供拷貝和移動版本窥妇,拷貝版本接受一個指向const的左值引用,移動版本接受一個指向非const的右值引用娩践。
  2. 通常我們可以在一個對象上調(diào)用成員活翩,而不用管該對象是左值或右值。C++允許向右值賦值翻伺,若想阻止該用法材泄,強(qiáng)制使左側(cè)運(yùn)算對象必須是左值,可在參數(shù)列表后放置引用限定符吨岭。
(s1+s2) = "abc"; // 雖然s1+s2返回右值拉宗,但它依舊可以調(diào)用拷貝賦值運(yùn)算符來實(shí)現(xiàn)賦值。
  1. 引用限定符可以是&&&辣辫,&指出this可以指向一個左值旦事,&&指出this可以指向一個右值。若const與引用限定符同時存在急灭,則const必須在前族檬,引用限定符必須在后。
  2. 若一個成員函數(shù)有引用限定符化戳,則具有相同參數(shù)列表的所有版本都必須有引用限定符。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末埋凯,一起剝皮案震驚了整個濱河市点楼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌白对,老刑警劉巖掠廓,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異甩恼,居然都是意外死亡蟀瞧,警方通過查閱死者的電腦和手機(jī)沉颂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悦污,“玉大人铸屉,你說我怎么就攤上這事∏卸耍” “怎么了彻坛?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長踏枣。 經(jīng)常有香客問我昌屉,道長,這世上最難降的妖魔是什么茵瀑? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任间驮,我火速辦了婚禮,結(jié)果婚禮上马昨,老公的妹妹穿的比我還像新娘竞帽。我一直安慰自己,他們只是感情好偏陪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布抢呆。 她就那樣靜靜地躺著,像睡著了一般笛谦。 火紅的嫁衣襯著肌膚如雪抱虐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天饥脑,我揣著相機(jī)與錄音恳邀,去河邊找鬼。 笑死灶轰,一個胖子當(dāng)著我的面吹牛谣沸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播笋颤,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼乳附,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伴澄?” 一聲冷哼從身側(cè)響起赋除,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎非凌,沒想到半個月后举农,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敞嗡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年颁糟,在試婚紗的時候發(fā)現(xiàn)自己被綠了航背。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡棱貌,死狀恐怖玖媚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情键畴,我是刑警寧澤最盅,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站起惕,受9級特大地震影響涡贱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惹想,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一问词、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嘀粱,春花似錦激挪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至娃磺,卻和暖如春薄湿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背偷卧。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工豺瘤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人听诸。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓坐求,卻偏偏與公主長得像,于是被迫代替她去往敵國和親晌梨。 傳聞我的和親對象是個殘疾皇子桥嗤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355