C/C++ tip2

explicit與隱式類型轉換

在調用函數時,如果傳給函數的參數和任何一個已經聲明的同名函數都不匹配,那么c++編譯器會想方設法地嘗試各種類型轉換以使函數調用能夠成立腰池,這其中包括我們用類型轉換操作符顯式定義的類型轉換和利用單參數構造函數來隱式進行類型轉換,其中后者往往是不符合預期的,可能導致不知不覺中引入bug喇肋。因此通常在聲明構造函數時,會使用explicit來進行修飾迹辐,以告訴編譯器不要嘗試使用該構造函數進行隱式的類型轉換蝶防。

C/C++的宏與字符串化

在宏定義中可以使用連續(xù)的兩個井號來粘連兩個字符串使其稱為一個新的字符串;此外還可以在宏參數前使用單個的井號來引用宏參數內容字符串:

#define DEFINE_METHOD(methodName)\
void test##methodName() {\
cout << "this is " << #methodName << endl;\
}

使用gcc時明吩,可以使用-E來查看展開后的結果间学。

鏈接

鏈接分為兩種,分別是內部鏈接(internal linkage)和外部鏈接(external linkage)印荔。
在一個程序中菱鸥,每個變量的存儲空間的位置和大小都必須被確定。編譯器根據諸如變量的類型和其所在的作用域這樣的信息來確定變量的存儲空間躏鱼。
內部鏈接只為當前正在被編譯的文件創(chuàng)建存儲空間氮采,不同的文件可以聲明相同名字的變量,但編譯器不會理會當前文件以外的變量定義染苛。聲明變量時使用static關鍵字來將變量的鏈接規(guī)則指定為內部鏈接鹊漠。
外部鏈接創(chuàng)建一片存儲空間,這塊存儲空間包含了所有文件中的外部變量茶行。在聲明變量時躯概,如果不加static關鍵字,變量就會被聲明并定義為外部鏈接變量畔师。使用extern聲明變量表示在當前文件中使用了一個外部變量娶靡,并告訴編譯器某個名字的變量存在于其它文件中,避免編譯器在編譯當前文件時抱怨找不到這個名字的變量的定義看锉。

常量

從前姿锭,在C中,定義常量必須使用預處理:
#define PI 3.14159
預處理定義常量有很多問題伯铣,比如PI并沒有具體的類型呻此,也不是一個變量,既無法在使用到PI的地方進行類型檢查腔寡,也無法獲取PI的地址焚鲜,而且PI的作用域是整個文件。
為了更清晰的常量語義,C++引入了命名常量的概念忿磅,并將其加入了C的標準糯彬。命名常量就是一個禁止改變值的變量。
C++規(guī)定葱她,在定義const常量時情连,必須同時指定常量的值。

volatile關鍵字

定義變量時使用volatile關鍵字表示該變量的值隨時可能被(非當前線程中執(zhí)行的代碼)改變览效,每次編譯器讀取該關鍵字修飾的值之前却舀,都必須從該變量所在內存地址中讀取,禁止了編譯器的一些優(yōu)化行為锤灿。多線程編程對可能被其它線程修改的變量可能使用該關鍵字挽拔,硬件編程中如果將某些變量映射到硬件寄存器地址,也需要使用該關鍵字以告訴編譯器每次都從變量實際地址讀取變量值但校。

類型轉換

用一對括號括起來的類型是C中的經典轉換運算符螃诅,通常也叫做強制類型轉換。使用強制類型轉換状囱,強迫編譯器將某個變量當作另一個類型而不進行任何檢查术裸,錯誤使用強制類型轉換可能導致程序bug。而且亭枷,強制類型轉換的括號很容易和程序的其它代碼混在一起袭艺,不便于檢查程序中哪些地方使用了強制類型轉換。
C++引入了新的類型轉換語法叨粘,分別是static_cast,const_cast, reinterpret_cast, dynamic_cast猾编。
static_cast用于明確定義的變換。能夠使用static_cast的地方都是可以不加static_cast直接依賴編譯器的隱式類型轉換的升敲,只不過編譯器可能會對隱式類型轉換發(fā)出警告答倡,使用static_cast顯示指定類型轉換告訴編譯器我們的確想要進行類型轉換,從而抑制警告驴党”衿玻可以用static_cast<void *>將一個指針轉換為無類型指針,也可以使用static_cast<int>將一個long類型變量轉換為int型變量港庄。這兩種轉換都可以由編譯器隱式進行倔既,使用static_cast抑制了編譯器對可能導致信息丟失的隱式類型轉換的警告。
const_cast用于將const變量或者volatile變量轉換為普通變量攘轩。僅此而已叉存。
reinterpret_cast,重解釋轉換度帮,是最不安全的一種轉換,這種轉換也是C風格強制類型轉換不受歡迎的主要原因,是最低級的位級別的轉換笨篷,將一個變量強制看作是另一個類型瞳秽。在Android Framework源碼中,經常通過static_cast將一個C++變量地址賦給java中的long變量率翅,隨后再拿到long變量并將其賦給void *指針练俐,然后通過reinterpret_cast將無類型指針轉換回這個指針指向的變量的真正類型。這里只能用reinterpret_cast來完成冕臭,但正常的C++編程中應該不需要用到這種類型轉換腺晾。
dynamic_cast是類型安全的向下轉換。C++中向上類型轉換是安全的辜贵,因此不需要顯示指定轉換悯蝉。但是向下類型轉換是不安全的,在必須進行向下類型轉換時托慨,使用dynamic_cast鼻由,可以讓編譯器檢查對象的實際類型是否和要轉換的類型匹配,只有當匹配時才會將執(zhí)行轉換并返回轉換后對象的指針厚棵,如果不匹配蕉世,就會返回0。但是婆硬,dynamic_cast由于需要進行額外的檢查(事實上dynamic_cast使用了RTTI特性)狠轻,大量的dynamic_cast可能導致性能問題。如果能夠確定(通常并不能確定)向下類型轉換的類型一定匹配彬犯,可以使用static_cast進行無檢查的向下類型轉換哈误。

句柄類和實現隱藏技術

C++中類定義往往定義在頭文件中,和實現分離躏嚎,這樣的C++庫在提供給其他人使用時蜜自,只要提供編譯好的庫文件和頭文件即可,這樣可以避免使用方看到不必要的細節(jié)卢佣,而且在實現變更時重荠,使用方可以只更新庫文件而不用同時更新頭文件。但是虚茶,類在定義時戈鲁,需要同時定義private方法,即使這些方法不應該被使用庫的程序員了解嘹叫。這是因為C++設計規(guī)定類只能在一個地方定義婆殿,并且必須一次定義完整的類。
為了將不希望用戶看到的功能隱藏起來罩扇,需要使用一種特殊的“實現隱藏”技巧婆芦,繞過C++的這個限制:

# 頭文件聲明ClassImpl但不定義
# PublicClass.h
class PublicClass {
    public:
        void get();
        void post();
    private:
        struct ClassImpl;
        ClassImpl* impl;
}
# 實現文件再定義并實現ClassImpl
# PublicClass.cpp
struct PublicClass::ClassImpl {
    ...
} 

RTTI

RTTI全稱是RunTime Type Information怕磨,即運行時類型信息,指的是程序運行時保留每個對象的類型信息消约。C++中的dynamic_cast和typeid運算符都是通過RTTI實現的肠鲫。啟用了RTTI就可以使用在<typeinfo>中定義的函數,在運行時查詢對象的類型信息了或粮,

C++的異常

C++標準提供了異常导饲,但很多人都不喜歡C++中的異常,因此不使用它氯材,gcc可以使用-fno-exception來禁止使用異常渣锦,指定了該選項后,源碼中如果使用了異常就會報錯氢哮。
可以在任何地方使用throw關鍵字拋出異常袋毙,可以使用try {…} catch (exceptionType e) {…}來捕獲異常,catch可以捕獲在try塊中拋出的和exceptionType類型匹配的異常命浴÷γǎ可以將exceptionType寫成...來捕獲所有類型的異常。

RAII

RAII全稱是Resource Acquisition Is Initialization生闲,是一種編程慣例媳溺,用于解決C++異常導致的資源管理問題。
RAII要求資源的有效期與對象的生命期嚴格綁定碍讯,對象的構造函數完成資源的獲取悬蔽,析構函數完成資源的釋放。這樣捉兴,只要對象能夠被正確地釋放蝎困,就不會出現資源泄漏問題。
C++標準庫提供的類如文件的輸入輸出流都是按照RAII實現的倍啥,因此通常不需要顯式調用關閉流函數禾乘,一旦流對象被析構,流使用的文件就會被析構函數關閉虽缕。但是在使用C的文件描述符時始藕,需要手動關閉文件描述符,如果希望C的文件描述符也能夠被自動地關閉氮趋,就需要使用RAII的一個變種RRID(Resource Release Is Destruction)方式管理對象:實現一個能夠指定析構函數的對象伍派,在文件描述符打開時同時在棧上創(chuàng)建一個這樣的對象,將析構函數指定為關閉文件描述符剩胁,這樣诉植,由于文件描述符同這個特殊的對象處于同一個作用域,特殊對象會和文件描述符一同失效昵观,其析構函數將保證文件描述符在同時被關閉晾腔。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末舌稀,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子建车,更是在濱河造成了極大的恐慌扩借,老刑警劉巖椒惨,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缤至,死亡現場離奇詭異碉克,居然都是意外死亡泊柬,警方通過查閱死者的電腦和手機曙旭,發(fā)現死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門镰吵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來码荔,“玉大人冤馏,你說我怎么就攤上這事婉徘“埔椋” “怎么了孽锥?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵嚼黔,是天一觀的道長。 經常有香客問我惜辑,道長唬涧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任盛撑,我火速辦了婚禮碎节,結果婚禮上,老公的妹妹穿的比我還像新娘抵卫。我一直安慰自己狮荔,他們只是感情好,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布介粘。 她就那樣靜靜地躺著殖氏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姻采。 梳的紋絲不亂的頭發(fā)上雅采,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音偎谁,去河邊找鬼总滩。 笑死,一個胖子當著我的面吹牛巡雨,可吹牛的內容都是我干的闰渔。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼铐望,長吁一口氣:“原來是場噩夢啊……” “哼冈涧!你這毒婦竟也來了茂附?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤督弓,失蹤者是張志新(化名)和其女友劉穎营曼,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體愚隧,經...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡蒂阱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了狂塘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片录煤。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖荞胡,靈堂內的尸體忽然破棺而出妈踊,到底是詐尸還是另有隱情,我是刑警寧澤泪漂,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布廊营,位于F島的核電站,受9級特大地震影響萝勤,放射性物質發(fā)生泄漏露筒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一纵刘、第九天 我趴在偏房一處隱蔽的房頂上張望邀窃。 院中可真熱鬧,春花似錦假哎、人聲如沸瞬捕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肪虎。三九已至,卻和暖如春惧蛹,著一層夾襖步出監(jiān)牢的瞬間扇救,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工香嗓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留迅腔,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓靠娱,卻偏偏與公主長得像沧烈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子像云,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內容