C++自制心得——開篇(缺省函數(shù)+函數(shù)重載)
前言:?
本心得只適合給那些擁有C語言基礎 (系統(tǒng)的自主學習過C語言(以找工作為目標,學校老師教的一律視作沒學),了解一些底層機制,用C實現(xiàn)過一些基本數(shù)據(jù)結(jié)構(gòu) (順序表、鏈表、隊列與棧、初等二叉樹、十大經(jīng)典排序等) ) 的人觀看。如果你不滿足上述條件就不要在這里浪費時間了。
本人在寫專欄上的技術力并不好,如果你覺得這篇文章在排版上有需要改進的地方可以在評論區(qū)留言并附上具體操作流程。
本人目前是大二在讀生,目前剛開始學習C++相關知識,如果有大佬發(fā)現(xiàn)哪里介紹的不對或者有疏漏歡迎在評論區(qū)留言,我盡量在發(fā)現(xiàn)的第一時間糾正。
好,廢話不多講,我們進入正題。?
缺省函數(shù)
test.cpp
slist.h
slist.cpp
這是一個簡單順序表的C代碼(.cpp環(huán)境下NULL有一些歷史遺留問題,換成了C++的nullptr),跑起來肯定沒問題,但是這里有個讓人不爽的地方。在slist.h里,我們用宏定義了一個默認初始化大小,一般情況下這個值是100,如果數(shù)據(jù)量多于這個值就會去擴容,這個邏輯看著還能接受。那如果出現(xiàn)了某些極端情況會怎么樣?讓代碼跑起來。

看看控制臺上滿屏幕的異地擴容,問題的嚴重性躍然紙上。哪怕不算頻繁拷貝帶來的性能浪費,這個方案所花費的時間也已經(jīng)達到了一步到位的近兩倍。顯然,在C語言里為了避免這一現(xiàn)象的發(fā)生,我們需要額外的函數(shù)來處理非默認情況下的空間開辟。
好吧,我承認我懶得再敲一個函數(shù),那么C++有沒有辦法用一個函數(shù)解決默認+非默認的情景?現(xiàn)在,請出我們的缺省函數(shù)(你可以認為缺省 == 默認),然后再跑一遍代碼。

slist.cpp(修改的部分)
slist.h
test.cpp
上面的代碼有點復雜,我們來看個簡單的。
test.cpp

顯然,缺省函數(shù)比常規(guī)函數(shù)多了一個備胎機制,如果你給了值,缺省函數(shù)就用你的值,你不給值,缺省函數(shù)就用備胎的值。
全缺省函數(shù)
函數(shù)當然可以有多個缺省值,如果一個函數(shù)的所有形參均擁有缺省值,那它就是全缺省函數(shù),such as:
test.cpp

我知道你想干什么,但是不行,你不能這么傳。

缺省函數(shù)存在多個缺省值時,只可從左向右傳參。
半缺省函數(shù)
常規(guī)形參與缺省形參也能一起用,這樣的函數(shù)也叫半缺省函數(shù)。
test.cpp

大膽的想法也可以收一收了,缺省函數(shù)存在多個缺省形參時,只可從右向左確定缺省形參,且所有的缺省形參必須在常規(guī)形參的右邊。
缺省函數(shù)的聲明與定義
在復雜工程里把某些函數(shù)聲明單獨封裝到頭文件里應該是個常識,不過在缺省函數(shù)里聲明定義分離是個大坑,舉個例子:

你要是這么寫,直接報錯

(⊙o⊙)…為什么不能這么寫,你看看下面這張圖。

wow,聲明和定義居然不一樣,編譯器要用哪個?編譯器不想搭理你還在你臉上甩了兩個重定義錯誤。所以正確的做法是這樣的:

在聲明處定義缺省形參,在定義處寫常規(guī)形參。至于為什么要在聲明處定義,很好理解。如果在定義處定義缺省形參,那么在傳參時部分傳法會與聲明沖突,結(jié)果就會報錯

那為什么編譯器可以通過缺省函數(shù)的聲明找到其定義,他們看上去可不太一樣,那就要用到函數(shù)重載的相關知識了。
函數(shù)重載
舉個例子,上面的Add函數(shù)要完成int + int的計算和double + double的計算,在C語言里只能給函數(shù)名加修飾符來區(qū)分,至于現(xiàn)在,上代碼。

所謂重載就是一詞多義,那么重載函數(shù)指的是函數(shù)名的一詞多義,這個概念應該很好理解。
如何構(gòu)成函數(shù)重載
1.形參類型不同

2.形參個數(shù)不同

3.形參順序不同

上述三個條件只需成立一個即可構(gòu)成函數(shù)重載。
注意事項:1. 對于第二點,與缺省函數(shù)聯(lián)用有可能出現(xiàn)調(diào)用歧義。


? ? ? ? ? ? ? ? 2. 對于第三點,請注意交換順序的是形參類型,而非形參名。
? ? ? ? ? ? ? ? 3. 其他條件相同,返回值類型不同的同名函數(shù)不構(gòu)成函數(shù)重載。(一旦構(gòu)成函數(shù)重? ? ? ? ? ? ? ? ? ? ? 載,你可以修改任意重載函數(shù)的返回值類型,并不是指返回值類型不同的同名函? ? ? ? ? ? ? ? ? ? ? 數(shù)不構(gòu)成函數(shù)重載)

函數(shù)重載核心原理——函數(shù)名稱修飾
正兒八經(jīng)學習過C語言的人應該知道,把工程里的代碼轉(zhuǎn)化成可執(zhí)行文件分兩步,編譯(編譯器)+ 鏈接(鏈接器),其中編譯分為三步,預處理(去注釋,宏替換,頭文件展開,條件編譯),編譯(檢查語法,生成匯編代碼),匯編(形成符號表,生成機器碼),鏈接階段進行符號表的合并與重定位,合并段表。

這里我故意寫了一個bug,還把文件后綴改成了.c,所以在C環(huán)境下這里被紅框框起來的部分就是函數(shù)func在符號表里的樣子,現(xiàn)在讓我們用CPP環(huán)境再試一次。

盡管vs2019的函數(shù)名修飾規(guī)則很奇葩,但它至少給了一點有用信息。C++符號表里的函數(shù)名多了很多修飾符,而且在上述例子中int,double位置的交換直接導致了N,H字母位置的交換。結(jié)論很明顯,C++在形成符號表的時候把形參以某種規(guī)則加入到函數(shù)名中,這樣做使得重載函數(shù)在符號表中的名字變的可以區(qū)分,如此一來重載函數(shù)也能擁有獨立地址,重載函數(shù)的自動匹配也就實現(xiàn)了。
現(xiàn)在我們就能回答缺省函數(shù)那里的問題了。因為函數(shù)名修飾規(guī)則不支持缺省參數(shù),所以在符號表里缺省函數(shù)的聲明與定義是一樣的,那么在鏈接的時候鏈接器會自動將聲明定義二合一,形成一個具有實際地址的函數(shù),這樣就不會出現(xiàn)鏈接錯誤了。
同理,重載函數(shù)不支持返回值重載也是因為函數(shù)名修飾規(guī)則不支持返回值,在符號表里就會出現(xiàn)兩個一樣的函數(shù)名,編譯器就會爆重定義錯誤。
這期的干貨很多,各位觀眾下去好好消化一下。下一期教程講引用,難度還要再加,諸位不要掉隊哦。