程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++拾遺(六)——復制控制,拾遺復制控制

C++拾遺(六)——復制控制,拾遺復制控制

編輯:C++入門知識

C++拾遺(六)——復制控制,拾遺復制控制


  年前忙了幾天,到現在才算是有空休息下來。先祝大家新年快樂,心想事成:)我也會發笑臉o.o

  這篇博文主要介紹定義一個類型的對象時的復制控制方式,這部分內容之前有一定的了解但又淺嘗辄止,始終感覺沒能找到要點。年前又拿起書細細品讀,算是有了一點新的了解。幾天前就想動筆了,一直沒時間,拖到現在。

  每種類型定義了創建該類型的對象時會發生什麼——構造函數定義了該類類型對象的初始化。類型還能控制復制、賦值或撤銷該類型的對象時會發生什麼——類通過特殊的成員函數:復制構造函數、賦值操作符和析構函數來控制這些行為。

  復制構造函數是一種特殊構造函數,具有單個形參,該形參(常用 const 修飾)是對該類類型的引用。當定義一個新對象並用一個同類型的對象對它進行初始化時,將顯式使用復制構造函數。當將該類型的對象傳遞給函數或函數返回該類型的對象時,將隱式使用復制構造函數。賦值操作符可以通過指定不同類型的右操作數而重載。右操作數為類類型的版本比較特殊:如果我們沒有編寫這種版本,編譯器將為我們合成一個。析構函數是構造函數的互補:當對象超出作用域或動態分配的對象被刪除時,將自動應用析構函數。析構函數可用於釋放對象時構造或在對象的生命期中所獲取的資源。不管類是否定義了自己的析構函數,編譯器都自動執行類中非static 數據成員的析構函數。復制構造函數、賦值操作符和析構函數總稱為復制控制。編譯器自動實現這些操作,但類也可以定義自己的版本。

復制構造函數

  • 只有單個形參,而且該形參是對本類類型對象的引用(常用 const 修飾),這樣的構造函數稱為復制構造函數。與默認構造函數一樣,復制構造函數可由編譯器隱式調用。
  • 如果我們沒有定義復制構造函數,編譯器就會為我們合成一個。與合成的默認構造函數不同,即使我們定義了其他構造函數,也會合成復制構造函數。合成復制構造函數的行為是,執行逐個成員初始化,將新對象初始化為原對象的副本。
  • 通常,定義復制構造函數最困難的部分在於認識到需要復制構造函數。只要能認識到需要復制構造函數,定義構造函數一般非常簡單。復制構造函數的定義與其他構造函數一樣:它與類同名,沒有返回值,可以(而且應該)使用構造函數初始化列表初始化新創建對象的成員,可以在函數體中做任何其他必要工作。
  • 有些類需要完全禁止復制。例如,iostream 類就不允許復制。如果想要禁止復制,似乎可以省略復制構造函數,然而,如果不定義復制構造函數,編譯器將合成一個。為了防止復制,類必須顯式聲明其復制構造函數為 private。然而,類的友元和成員仍可以進行復制。如果想要連友元和成員中的復制也禁止,就可以聲明一個(private)復制構造函數但不對其定義。聲明而不定義成員函數是合法的,但是,使用未定義成員的任何嘗試將導致鏈接失敗。通過聲明(但不定義)private 復制構造函數,可以禁止任何復制類類型對象的嘗試:用戶代碼中復制嘗試將在編譯時標記為錯誤,而成員函數和友元中的復制嘗試將在鏈接時導致錯誤。

賦值操作符

  • 重載操作符是一些函數,其名字為 operator 後跟著所定義的操作符的符號。因此,通過定義名為 operator= 的函數,我們可以對賦值進行定義。像任何其他函數一樣,操作符函數有一個返回值和一個形參表。形參表必須具有與該操作符數目相同的形參(如果操作符是一個類成員,則包括隱式 this 形參。賦值是二元運算,所以該操作符函數有兩個形參:第一個形參對應著左操作數,第二個形參對應右操作數。大多數操作符可以定義為成員函數或非成員函數。當操作符為成員函數時,它的第一個操作數隱式綁定到 this 指針。有些操作符(包括賦值操作符)必須是定義自己的類的成員。因為賦值必須是類的成員,所以 this 綁定到指向左操作數的指針。因此,賦值操作符接受單個形參,且該形參是同一類類型的對象。右操作數一般作為 const 引用傳遞。賦值操作符的返回類型應該與內置類型賦值運算返回的類型相同。內置類型的賦值運算返回對右操作數的引用,因此,賦值操作符也返回對同一類類型的引用。

析構函數

  • 構造函數的一個用途是自動獲取資源。例如,構造函數可以分配一個緩沖區或打開一個文件,在構造函數中分配了資源之後,需要一個對應操作自動回收或釋放資源。析構函數就是這樣的一個特殊函數,它可以完成所需的資源回收,作為類構造函數的補充。
  • 許多類不需要顯式析構函數,尤其是具有構造函數的類不一定需要定義自己的析構函數。僅在有些工作需要析構函數完成時,才需要析構函數。析構函數通常用於釋放在構造函數或在對象生命期內獲取的資源。如果類需要析構函數,則它也需要賦值操作符和復制構造函數,這是一個有用的經驗法則。這個規則常稱為三法則,指的是如果需要析構函數,則需要所有這三個復制控制成員。與復制構造函數或賦值操作符不同,編譯器總是會為我們合成一個析構函數。合成析構函數按對象創建時的逆序撤銷每個非 static 成員,因此,它按成員在類中聲明次序的逆序撤銷成員。對於類類型的每個成員,合成析構函數調用該成員的析構函數來撤銷對象。
  • 析構函數是個成員函數,它的名字是在類名字之前加上一個代字號(~),它沒有返回值,沒有形參。因為不能指定任何形參,所以不能重載析構函數。雖然可以為一個類定義多個構造函數,但只能提供一個析構函數,應用於類的所有對象。析構函數與復制構造函數或賦值操作符之間的一個重要區別是,即使我們編寫了自己的析構函數,合成析構函數仍然運行。

管理指針成員

  通過不同的復制控制策略,可以為指針成員實現不同的行為。其中一種便是智能指針。關於這部分內容,我打算在下一篇專門講指針的博客中詳加闡述(如果有細心的讀者看過本系列博客前文,應該注意到我到現在都沒有提及指針這一不可避免的特性)。本文內容未完,還有一個消息處理實例沒有展示,有時間再更新,現在有點累哈哈。

  最後再祝大家新年快樂!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved