C/C++ tips1

在學(xué)習(xí)C++的過程中派歌,發(fā)現(xiàn)C++真是一個(gè)龐大、復(fù)雜含滴、微妙的語言鬼雀。C++雖然有很多設(shè)計(jì)問題,但是這些年來大家找到了各種各樣的方法來克服這些問題蛙吏,一些特殊的寫法初看可能很奇怪,但實(shí)際上這些寫法都是經(jīng)過時(shí)間考驗(yàn)的專門用來解決某一個(gè)問題的經(jīng)典方法鞋吉。如果對(duì)C++不是特別熟悉鸦做,很容易在C++源碼中迷失。
我已經(jīng)先后多次學(xué)習(xí)C++谓着,但由于我的工作和學(xué)習(xí)中大部分時(shí)間都在使用java泼诱,因此總是學(xué)了以后,一段時(shí)間沒有用到就忘了一些內(nèi)容赊锚。這次我重新學(xué)習(xí)C++治筒,并將有趣的,尤其是C/C++獨(dú)有的知識(shí)點(diǎn)記錄在此舷蒲,以便查閱耸袜。這里記錄的內(nèi)容僅僅是對(duì)每個(gè)主題的簡單介紹,不過對(duì)主題有了解的人應(yīng)該能夠從這些簡單介紹中回憶起相關(guān)內(nèi)容牲平,更加詳細(xì)的解釋說明已有很多經(jīng)典著作如《Thinking In C++》和《Effective C++》等給出堤框。

c++的設(shè)計(jì)哲學(xué)

Is cpp the world’s most elegant language?
c是一門小巧、優(yōu)雅纵柿、易于上手的語言蜈抓。
c++不是一門優(yōu)雅的語言,而是一門實(shí)用的語言昂儒。
如果想要讓一個(gè)編程的初學(xué)者對(duì)編程喪失信心沟使,c++絕對(duì)是最佳備選之一。
c++中充斥著undefined behavior渊跋,有著各種各樣的編程風(fēng)格腊嗡,最近的c++委員會(huì)每一次升級(jí)語言標(biāo)準(zhǔn)都使得c++變得更加復(fù)雜更加難以理解着倾,各種編譯器對(duì)同一套源碼進(jìn)行編譯,甚至有矛盾的編譯結(jié)果叽唱。雖然如此屈呕,不過c++畢竟經(jīng)過了這么多年的考驗(yàn),它的強(qiáng)大是毋庸置疑的棺亭。如果多花些時(shí)間學(xué)習(xí)和熟悉C++虎眨,就能夠掌握這個(gè)強(qiáng)大的工具。

extern關(guān)鍵字的作用

extern關(guān)鍵字用在聲明變量時(shí)镶摘,可以告訴編譯器這個(gè)變量在運(yùn)行時(shí)會(huì)由其它程序定義嗽桩。
extern關(guān)鍵字用在extern “C” {…}時(shí),可以讓編譯器以C的方式處理塊內(nèi)代碼凄敢,并且塊內(nèi)必須是標(biāo)準(zhǔn)的C程序碌冶。

c++頭文件:尖括號(hào)和雙引號(hào)的區(qū)別

使用尖括號(hào)引用的頭文件只在search path中尋找,也就是系統(tǒng)默認(rèn)頭文件目錄和我們通過-L指定的目錄涝缝。使用雙引號(hào)引用的頭文件先相對(duì)于文件所在目錄查找扑庞,如果找不到再去search path中尋找。
早期不同的操作系統(tǒng)規(guī)定了不同的文件命名規(guī)范拒逮,某些系統(tǒng)甚至規(guī)定文件名不能超過8個(gè)字符罐氨,為了兼容這些不同的操作系統(tǒng),c++標(biāo)準(zhǔn)規(guī)定通過尖括號(hào)引用頭文件時(shí)可以不帶文件后綴滩援,編譯器應(yīng)當(dāng)按照當(dāng)前系統(tǒng)的標(biāo)準(zhǔn)去查找被引用的文件栅隐。
c++標(biāo)準(zhǔn)沒有強(qiáng)制要求用尖括號(hào)引用的頭文件必須不帶后綴,這使C程序能夠直接被C++編譯玩徊,因?yàn)镃程序在引用頭文件時(shí)都是帶.h后綴的租悄。c++標(biāo)準(zhǔn)針對(duì)C頭文件規(guī)定了一種特殊的引用格式,可以用 include <cstdio> 替換 include <stdio.h>恩袱。這樣泣棋,可以較為方便地從引用文件的名字看出被引用的是C程序頭文件,同時(shí)引用方式和新c++頭文件不帶后綴的引用方式保持一致憎蛤。

覆蓋標(biāo)準(zhǔn)庫函數(shù)的實(shí)現(xiàn)

編譯大體上分為編譯和鏈接兩個(gè)步驟外傅,編譯器首先將源代碼編譯成目標(biāo)文件,然后將目標(biāo)文件同庫文件鏈接以解析目標(biāo)文件中的未定引用俩檬。
連接器在鏈接庫文件時(shí)萎胰,每發(fā)現(xiàn)一個(gè)未定引用就去指定的庫文件列表中查找被引用的符號(hào)。我們可以利用這個(gè)特性棚辽,在我們自己的庫文件中實(shí)現(xiàn)同標(biāo)準(zhǔn)庫函數(shù)同名的函數(shù)技竟,并讓連接器在查找?guī)煳募r(shí)優(yōu)先查詢我們自己的庫文件,這樣鏈接器就會(huì)在我們的庫文件中發(fā)現(xiàn)標(biāo)準(zhǔn)庫函數(shù)的實(shí)現(xiàn)并直接使用該實(shí)現(xiàn)屈藐,不會(huì)繼續(xù)去標(biāo)準(zhǔn)庫文件中查找標(biāo)準(zhǔn)實(shí)現(xiàn)了榔组。

被偷偷鏈接的標(biāo)準(zhǔn)庫

在將目標(biāo)文件編譯成可執(zhí)行程序時(shí)熙尉,編譯器偷偷地幫我們鏈接了一些庫文件。其中一個(gè)庫文件是啟動(dòng)模塊搓扯,正是在該庫文件中引用并調(diào)用了main函數(shù)检痰,所以在缺少main函數(shù)時(shí),編譯器會(huì)抱怨說存在未解析的main引用锨推。當(dāng)執(zhí)行可執(zhí)行程序時(shí)铅歼,實(shí)際上是從啟動(dòng)模塊庫文件的一個(gè)入口處開始執(zhí)行的。
此外换可,標(biāo)準(zhǔn)庫文件也會(huì)被偷偷地鏈接椎椰,從而讓我們能夠直接引用標(biāo)準(zhǔn)庫頭文件,而不用指明link標(biāo)準(zhǔn)庫文件沾鳄。

struct與POD

C++中慨飘,struct和class幾乎是同義詞,唯一的區(qū)別在于默認(rèn)的訪問控制級(jí)別译荞。class甚至可以繼承自struct瓤的,struct也可以繼承自class。
通常使用class來聲明用到了面向?qū)ο笏枷氲念愅碳撸褂胹truct來聲明簡單的數(shù)據(jù)類堤瘤,簡單數(shù)據(jù)類通常被叫做Plain Old Data,即POD浆熔。POD是和C兼容的struct,是指對(duì)象足夠簡單桥帆,能夠直接將其內(nèi)存寫入文件医增,并隨后還能夠直接恢復(fù)的對(duì)象。
比如:

// 寫入文件
struct date *object=malloc(sizeof(struct date));
strcpy(object->day,"Good day");
object->month=6;
object->year=2013;
FILE * file= fopen("output", "wb");
if (file != NULL) {
    fwrite(object, sizeof(struct date), 1, file);
    fclose(file);
}
// 從文件讀出
struct date *object2=malloc(sizeof(struct date));
FILE * file= fopen("output", "rb");
if (file != NULL) {
    fread(object2, sizeof(struct date), 1, file);
    fclose(file);
}
printf("%s/%d/%d\n",object2->day,object2->month,object2->year);

變長結(jié)構(gòu)體

諸如這樣的結(jié)構(gòu)體稱為變長結(jié)構(gòu)體老虫,其中X[0]在編譯期長度為0叶骨,其空間在運(yùn)行時(shí)被動(dòng)態(tài)地確定,這是GNU軟件的慣常做法:

     struct line {
       int length;            // 在這個(gè)屬性中記錄contents的長度
       char contents[0];      // 可變長數(shù)組祈匙,由于其長度聲明為0忽刽,因此在struct中不占空間
     };
    
     struct line *thisline = (struct line *)
     malloc (sizeof (struct line) + this_length);
     thisline->length = this_length;

     struct f1 {
       int x; int y[];
     } f1 = { 1, { 2, 3, 4 } };
    
     struct f2 {
       struct f1 f1; int data[3];
     } f2 = { { 1 }, { 2, 3, 4 } };

注意在使用這種技巧時(shí),要注意可變長數(shù)組一定要出現(xiàn)在struct的末尾夺欲。當(dāng)這樣的struct被其他struct包含時(shí)跪帝,不可以使用這種技巧,因?yàn)檫@時(shí)在內(nèi)層struct末尾之后的是外層struct的其它屬性些阅,利用這種技巧將導(dǎo)致外層struct的屬性被覆蓋伞剑。

typedef和typename

typename常常和typedef一同使用,比如
typedef typename traits_type::char_type value_type;
typename的作用有兩個(gè)市埋,第一是用來替代泛型編程中的class關(guān)鍵字以更好地表達(dá)范型的語義黎泣;第二是用來告訴編譯器某個(gè)符號(hào)是一個(gè)類型而非一個(gè)變量恕刘。上面的例子中typename就告訴編譯器traits_type::char_type是一個(gè)類型,而不是traits_type類中的一個(gè)變量抒倚、方法或者別的什么東西褐着。

運(yùn)算符重載

運(yùn)算符重載的規(guī)則比較復(fù)雜,這里的復(fù)雜體現(xiàn)在可重載的運(yùn)算符的數(shù)量多托呕、不同運(yùn)算符重載需要遵循不同的規(guī)則:
c++支持的運(yùn)算符重載有如下這些:

  1. 正常的可重載運(yùn)算符:operator op含蓉,op可以取:
    1. 加減乘除镣陕、取余谴餐、冥乘:+ - * / % ? ++ --
    2. 與、或呆抑、非岂嗓、取反、邏輯與鹊碍、邏輯或: & | ~ ! && ||
    3. 賦值厌殉、運(yùn)算并賦值、大于小于等于: = < > += -= *= /= %= ?= &= |= != <= >=
    4. 移位侈咕、移位并賦值:<< >> >>= <<= ==
    5. 其它特殊運(yùn)算符:, ->* -> ( ) [ ]
  2. 類型轉(zhuǎn)換函數(shù):operator Type
  3. 內(nèi)存分配函數(shù):operator new, operator new [ ]
  4. 內(nèi)存釋放函數(shù):operator delete, operator delete [ ]
  5. 用戶定義字面量:operator “”

雖然運(yùn)算符重載可以提供任意的返回類型公罕,但是為了可讀性,不同的運(yùn)算符重載應(yīng)該遵循各自的規(guī)則:

  1. 二元運(yùn)算符應(yīng)該具有反身性耀销,即a+b和b+a應(yīng)該調(diào)用同樣的運(yùn)算符函數(shù)楼眷,為此二元運(yùn)算符應(yīng)該實(shí)現(xiàn)為非成員友元函數(shù)。順帶一提熊尉,二元運(yùn)算符的定義很微妙罐柳,當(dāng)定義如+這樣的運(yùn)算符時(shí),具體定義的是二元成員運(yùn)算符還是一元非成員運(yùn)算符取決于該定義是否有friend作為修飾狰住。如果這樣定義:T operator+(const T& rval);张吉,定義的是類型T的二元加法運(yùn)算符函數(shù),加法的第一個(gè)操作數(shù)是this催植;如果在前面加上一個(gè)friend肮蛹,即friend T operator+(const T& rval);,定義的就是一個(gè)非成員的友元函數(shù)创南,這個(gè)函數(shù)是一元運(yùn)算符+伦忠。
  2. 賦值運(yùn)算符總是應(yīng)當(dāng)檢查自賦值,并在自賦值時(shí)什么都不做稿辙。
  3. 指針運(yùn)算符應(yīng)該返回一個(gè)重載了指針運(yùn)算符的對(duì)象或者一個(gè)指針缓苛,如果返回了一個(gè)重載了指針運(yùn)算符的對(duì)象,當(dāng)調(diào)用指針運(yùn)算符時(shí),會(huì)遞歸地調(diào)用指針運(yùn)算符未桥,直到最后返回了真正的指針笔刹,然后在這個(gè)指針上調(diào)用指針運(yùn)算符后面指定的方法。
  4. 使用運(yùn)算符重載主要是為了更好的可讀性冬耿,一些數(shù)學(xué)相關(guān)的類型如矩陣舌菜、行列式進(jìn)行運(yùn)算符重載可以很好地增加可讀性;一些智能指針類也可以通過重載賦值運(yùn)算符和指針運(yùn)算符來實(shí)現(xiàn)對(duì)包裝對(duì)象的透明訪問亦镶。
  5. 雖然可以重載指針成員訪問運(yùn)算符日月、逗號(hào)運(yùn)算符,但是重載它們不僅很麻煩而且通常會(huì)破壞可讀性缤骨。就算是std::shared_ptr也沒有重載指針成員訪問運(yùn)算符->*爱咬。

類成員指針和成員指針運(yùn)算符

在C中,每個(gè)函數(shù)都有地址绊起,可以用一個(gè)指針指向這個(gè)函數(shù)精拟。
在C++中,同樣如此虱歪,不過C++引入了對(duì)象的概念蜂绎,一個(gè)方法有可能是全局函數(shù)、類靜態(tài)函數(shù)或者類成員函數(shù)笋鄙。對(duì)于全局函數(shù)和類靜態(tài)函數(shù)师枣,只要知道這個(gè)函數(shù)的地址就可以通過函數(shù)指針來訪問它;但是對(duì)于成員函數(shù)來說萧落,光是知道函數(shù)地址是不夠的践美,它還需要和一個(gè)this指針關(guān)聯(lián),因此要想調(diào)用成員函數(shù)指針找岖,需要在調(diào)用時(shí)指定和它關(guān)聯(lián)的this指針拨脉,c++特意定義了兩個(gè)運(yùn)算符:.和->兩個(gè)運(yùn)算符,這兩個(gè)運(yùn)算符叫做Pointer-To-Member operator宣增,也就是成員指針運(yùn)算符。
假設(shè)method是一個(gè)成員函數(shù)指針矛缨,如auto method = T::method爹脾,如果t是一個(gè)對(duì)象,就可以通過t.method()來調(diào)用method箕昭,且在函數(shù)中可以通過this來引用&t灵妨;如果pt是一個(gè)指針,就可以通過pt->method()來調(diào)用method落竹,且在函數(shù)中可以通過this來引用pt泌霍。

Functor,函數(shù)對(duì)象

Functor是指重載了operator()的類,這樣的類對(duì)象能夠表現(xiàn)得像是一個(gè)函數(shù)一樣朱转,因此得名functor蟹地。functor在使用上和函數(shù)指針相似,但functor由于自身是一個(gè)類的實(shí)例藤为,因此可以保持狀態(tài)怪与,使用functor,可以實(shí)現(xiàn)類似函數(shù)式編程的代碼風(fēng)格缅疟。
新的C++標(biāo)準(zhǔn)引入了lambda用于實(shí)現(xiàn)類似的行為分别,大多數(shù)情況下應(yīng)該可以直接使用lambda。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末存淫,一起剝皮案震驚了整個(gè)濱河市耘斩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桅咆,老刑警劉巖括授,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異轧邪,居然都是意外死亡刽脖,警方通過查閱死者的電腦和手機(jī)珍德,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門斤吐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耕赘,“玉大人兑巾,你說我怎么就攤上這事院塞〉∨穑” “怎么了入蛆?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵屋摔,是天一觀的道長简十。 經(jīng)常有香客問我檬某,道長,這世上最難降的妖魔是什么螟蝙? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任恢恼,我火速辦了婚禮,結(jié)果婚禮上胰默,老公的妹妹穿的比我還像新娘场斑。我一直安慰自己,他們只是感情好牵署,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布漏隐。 她就那樣靜靜地躺著,像睡著了一般奴迅。 火紅的嫁衣襯著肌膚如雪青责。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音脖隶,去河邊找鬼扁耐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛浩村,可吹牛的內(nèi)容都是我干的做葵。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼心墅,長吁一口氣:“原來是場噩夢啊……” “哼酿矢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起怎燥,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤瘫筐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后铐姚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體策肝,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年隐绵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了之众。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡依许,死狀恐怖棺禾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情峭跳,我是刑警寧澤膘婶,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蛀醉,受9級(jí)特大地震影響悬襟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拯刁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一脊岳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧垛玻,春花似錦割捅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巫糙。三九已至朗儒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背醉锄。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國打工乏悄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人恳不。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓檩小,卻偏偏與公主長得像,于是被迫代替她去往敵國和親烟勋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子规求,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容