構(gòu)造函數(shù)是 C++ 類的重要組成部分括享。
當(dāng)我們定義變量時習(xí)慣立即對其進行初始化河闰,而非先定義、再賦值:
就對象的數(shù)據(jù)成員而言锨匆,初始化和賦值也有類似的區(qū)別崭别,如果沒有構(gòu)造函數(shù)的初始值列表中顯式地初始化成員,則該成員將在構(gòu)造函數(shù)體之前執(zhí)行默認初始化恐锣。
當(dāng)構(gòu)造函數(shù)完成后茅主,數(shù)據(jù)成員的值相同,之前版本初始化了它的數(shù)據(jù)成員侥蒙,現(xiàn)在版本對數(shù)據(jù)成員執(zhí)行了賦值操作暗膜。
有時可以忽略數(shù)據(jù)成員初始化和賦值之間的差異。如果成員是 const 或引用鞭衩,必須將其初始化学搜。當(dāng)成員屬于某種類類型且該類沒有定義默認構(gòu)造函數(shù)時娃善,也必須將這個成員初始化。
和其它常量對象或者引用一樣瑞佩,成員 ci 和 ri 都必須被初始化聚磺,因此,若沒有為它們提供構(gòu)造函數(shù)初始值的話將引發(fā)錯誤:
隨著構(gòu)造體函數(shù)一開始執(zhí)行炬丸,初始化就完成了瘫寝,初始化 const 或者引用類型的數(shù)據(jù)成員的唯一機會就是通過構(gòu)造函數(shù)初始值。
在構(gòu)造函數(shù)初始值中每個成員只能出現(xiàn)一次稠炬,否則給同一個成員賦兩個不同的初始值有什么意義焕阿?
構(gòu)造函數(shù)初始值列表只能說明用于初始化成員的值,而不限定初始化的具體執(zhí)行順序首启。成員的初始化順序與它們在類定義中的出現(xiàn)順序一致暮屡。構(gòu)造函數(shù)初始值列表中初始值的前后位置關(guān)系不會影響實際的初始化順序。
一般來說毅桃,初始化的順序沒什么特別要求褒纲,不過如果一個成員是用另一個成員來初始化的,那么這兩個成員的初始化順序就很關(guān)鍵了钥飞。
最好用構(gòu)造函數(shù)的參數(shù)作為成員的初始值莺掠,而盡量避免使用同一個對象的其它成員。這樣的好處是可以不必考慮成員的初始化順序读宙。
若一個構(gòu)造函數(shù)為所有參數(shù)都提供了默認實參彻秆,則它實際上也定義了默認構(gòu)造函數(shù)。
我們可以定義所謂的委托構(gòu)造函數(shù)论悴,一個委托構(gòu)造函數(shù)使用它所屬的類的其他構(gòu)造函數(shù)執(zhí)行它自己的初始化過程掖棉,或者說它把它自己的一些職責(zé)委托給了其它構(gòu)造函數(shù)。
一個委托構(gòu)造函數(shù)也有一個成員初始值的列表和一個函數(shù)體膀估。在委托構(gòu)造函數(shù)內(nèi)幔亥,成員初始值列表只有一個唯一的入口,就是類名本身察纯。和其它成員初始值一樣帕棉,類名后面緊跟圓括號括起來的參數(shù)列表,參數(shù)列表必須與類中另外一個構(gòu)造函數(shù)匹配饼记。
此類中香伴,除了一個構(gòu)造函數(shù)外其它的都委托了它們的工作,第一個構(gòu)造函數(shù)接收三個實參具则,使用這些實參初始化數(shù)據(jù)成員即纲,然后結(jié)束工作。
定義默認構(gòu)造函數(shù)零七使用三參數(shù)的構(gòu)造函數(shù)完成初始化過程博肋,它也無須執(zhí)行其它任務(wù)低斋,這一點從空的構(gòu)造函數(shù)體能看的出來蜂厅。接受一個 string 的構(gòu)造函數(shù)同樣委托給了三參數(shù)的版本。
接受 istream& 的構(gòu)造函數(shù)也是委托構(gòu)造函數(shù)膊畴,它委托給了默認構(gòu)造函數(shù)掘猿,默認構(gòu)造函數(shù)又接著委托給三參數(shù)構(gòu)造函數(shù)。當(dāng)這些委托的構(gòu)造函數(shù)執(zhí)行完后唇跨,接著執(zhí)行 istream& 構(gòu)造函數(shù)體的內(nèi)容稠通。調(diào)用 read 函數(shù)讀取給定的 istream 。
當(dāng)一個構(gòu)造函數(shù)委托給另一個構(gòu)造函數(shù)時买猖,受委托的構(gòu)造函數(shù)的初始值列表和函數(shù)體被一次執(zhí)行改橘。在 sales_data 類中,受委托的構(gòu)造函數(shù)體恰好時空的政勃。膠乳函數(shù)體包含代碼的話唧龄,將先執(zhí)行這些代碼,然后控制權(quán)才會交還給委托者的函數(shù)體奸远。
當(dāng)對象被默認初始化或值初始化時自動執(zhí)行默認構(gòu)造函數(shù)。
類必須包含一個默認構(gòu)造函數(shù)以便在上述情況下使用讽挟,其中的大多數(shù)情況非常容易判斷懒叛。
不那么明顯的一種情況時類的某些數(shù)據(jù)成員缺少默認構(gòu)造函數(shù)。
但使用時耽梅,編譯器將報錯薛窥,提示我們不能對函數(shù)使用成員訪問運算符。盡管想聲明一個默認初始化對象眼姐, obj 實際的含義卻是一個不接受任何參數(shù)的函數(shù)并且其返回值是 sales_data 類型的對象诅迷。
如果想定義一個使用默認構(gòu)造函數(shù)進行初始化的對象,正確的方法是去掉對象名之后的空括號众旗。
我們可以為類定義隱式轉(zhuǎn)換規(guī)則
如果構(gòu)造函數(shù)只接受一個實參罢杉,則它實際上定義了轉(zhuǎn)換為此類類型的隱式轉(zhuǎn)換機制,有時我們把這種構(gòu)造函數(shù)稱作轉(zhuǎn)換構(gòu)造函數(shù)贡歧。
能通過一個實參調(diào)用的構(gòu)造函數(shù)定義了一條從構(gòu)造函數(shù)的參數(shù)類型向類型隱式轉(zhuǎn)換的規(guī)則滩租。
在 sales_data 類中,接受 string 的構(gòu)造函數(shù)和接受 istream 的構(gòu)造函數(shù)分別定義了從這兩種類型向 sales_data 隱式轉(zhuǎn)換的規(guī)則利朵。在需要使用 sales_data 的地方律想,我們可以使用 string 、 istream 作為替代绍弟。
編譯器指揮自動地執(zhí)行一步類型轉(zhuǎn)換
是否需要從 string 到 sales_data 的轉(zhuǎn)換依賴于我們對用戶使用該轉(zhuǎn)換的看法技即。
在要求隱式轉(zhuǎn)換的程序上下文中,我們可以通過將構(gòu)造函數(shù)聲明為? explicit 加以阻止
此時樟遣,沒有任何構(gòu)造函數(shù)能用于隱式地創(chuàng)建 sales_data 對象而叼,之前的兩種用法都無法通過編譯身笤。
關(guān)鍵字 explicit 只對一個實參的構(gòu)造函數(shù)有效。需要多個實參的構(gòu)造函數(shù)不能用于執(zhí)行隱式轉(zhuǎn)化澈歉,所以無須將這些構(gòu)造函數(shù)指定為 explicit 展鸡。
只能在類內(nèi)聲明構(gòu)造函數(shù)時使用 explicit ,在外部定義時不應(yīng)重復(fù)埃难。
發(fā)生隱式轉(zhuǎn)換的一種情況時當(dāng)執(zhí)行拷貝形式的初始化時 ( = )莹弊,我們只能使用直接初始化而不能使用 explicit 構(gòu)造函數(shù)。
編譯器不會將 explicit 的構(gòu)造函數(shù)用于隱式轉(zhuǎn)換過程涡尘,但是我們可以使用這樣的構(gòu)造函數(shù)顯示地強制進行轉(zhuǎn)換
第一個調(diào)用中忍弛,直接使用 sales_data 的構(gòu)造函數(shù),該調(diào)用通過接受 string 的構(gòu)造函數(shù)創(chuàng)建了一個臨時的 sales_data 對象考抄。
第二個調(diào)用中细疚,使用 static_cast 執(zhí)行了顯式的而非隱式的轉(zhuǎn)換。其中川梅,static_cast 使用 istream 構(gòu)造函數(shù)創(chuàng)建了一個臨時的sales_data 對象疯兼。
聚合類使得用戶可以直接訪問其它成員,并且具有特殊的初始化語法形式贫途。
我們可以提供一個花括號括起來的成員初始值列表吧彪,并用它初始化聚合類的數(shù)據(jù)成員
初始值的順序必須與聲明的順序一致。
與初始化數(shù)組元素的規(guī)則一樣丢早,如果初始值列表中的元素個數(shù)少于類的成員數(shù)量姨裸,則靠后的成員被值初始化。初始值列表的元素個數(shù)絕對不能超過類的成員數(shù)量怨酝。
除了算術(shù)類型傀缩、引用和指針外,某些類也是字面值類型农猬。
字面值類型的類可能含有 constexpr 函數(shù)成員赡艰,這樣的成員必須符合 constexpr 函數(shù)的所有要求,他們是隱式 const 的盛险。
數(shù)據(jù)成員都是字面值類型的聚合類時字面值常量類瞄摊。若類不是一個聚合類钾恢,但它符合下述要求没炒,則也是一個字面值常量類醋闭。
盡管構(gòu)造函數(shù)不能時 const 的属百,但字面值常量類的構(gòu)造函數(shù)可以時 constexpr 函數(shù)茅信,一個字面值常量類必須至少提供一個 constexpr 構(gòu)造函數(shù)枯芬。
constexpr 構(gòu)造函數(shù)可以聲明成 =default 的形式掘殴。否則 constexpr 構(gòu)造函數(shù)就必須即符合構(gòu)造函數(shù)的要求匕荸,又符合 consrexpr 函數(shù)的要求。所以 constexpr 構(gòu)造函數(shù)體一般是空的祟牲。
constexpr 構(gòu)造函數(shù)必須初始化所有數(shù)據(jù)成員隙畜,初始值或者使用 constexpr 構(gòu)造函數(shù)或是一條常量表達式。