C++第九章筆記
和C一樣,C++為記憶體儲存資料方面提供了多種選擇,可以選擇資料保留在記憶體中的時間(生命域)以及程式的哪一部分可以訪問資料(作用域和連結)等。
單獨編譯
和C語言一樣,C++也允許甚至鼓勵程式設計師將元件函式放在獨立的檔案中。可以單獨編譯這些檔案然後將他們連結成可執行的程式。(IDE包含編譯器和連結器的原由吧)。
標頭檔案:包含結構宣告和使用這些結構的函式的原型
原始碼檔案:包含與結構有關的函式的程式碼
原始碼檔案:包含呼叫與結構相關的函式程式碼
不要將函式定義或變數宣告放在標頭檔案中,對於簡單的情況是可行的,但是如果存在重複包含標頭檔案的檔案,則會出現一個程式中將包含同一個函式的兩個定義,除非函式式內聯的,否則這將出錯。
標頭檔案經常包含的內容:
- 函式原型
- 使用define或const定義的符號常量
- 結構宣告
- 類宣告
- 模板宣告
- 行內函數
將結構宣告放在標頭檔案中是可以的,因為它們不建立變數,而只是在原始碼檔案中宣告結構變數時,告訴編譯器如何建立該結構變數。同樣,模板宣告不是將被編譯的程式碼,它們指示編譯器如何生成與原始碼中函式呼叫相匹配的函式定義。被宣告為const的資料和行內函數有特殊的連結屬性,因此可以放在標頭檔案。
儲存持續性、作用域和連結性
在C++11中,有四種不同的方案來儲存資料,這些方案的區別在於資料保留在記憶體中的時間。
- 自動儲存持續性(自動變數)
- 靜態儲存持續性(靜態變數,全域性變數)
- 執行緒儲存持續性(C++11,使用關鍵字thread_local)
- 動態儲存持續性(動態記憶體)
作用域描述了名稱在檔案的多大範圍內可見。
作用域為區域性的變數只在定義它的程式碼塊中可用。程式碼塊是由花括號括起來的一系列語句。作用域為全域性(也叫做檔案作用域)的變數在定義位置到檔案結尾之間都可用。自動變數的作用域為區域性,靜態變數的作用域是全域性還是區域性取決於是如何定義的:
鏈結性描述了名稱如何在不同單元間共享:外部連結在檔案間共享,內部連結只能有一個檔案中的函式共享,自動變數沒有鏈結性,因為它們不能被共享。
mutable說明符指出,即使結構變數為const,其某個成員也可以被修改
struct data
{
char name[30];
mutable int accesses;
...
};
又是一個神器。。
new支援定位操作,
char s[100];
int *p = new(s) int[10];
便可以在s的空間中申明出一個10個元素的int陣列。神奇。
但是,因為畢竟不是在堆中開的記憶體,用delete會錯誤!
名稱空間
在C++中,名稱可以是變數、函式、結構、列舉、類以及類和結構的成員。當隨著專案的增大,名稱相互衝突的可能性增大。使用多個廠商的類庫時,可能導致名稱衝突。因此有了名稱空間。
傳統的C++名稱空間,
宣告區域是可以在其中宣告的區域比如函式外的全域性變數。
潛在作用域,變數在潛在作用域從宣告點開始,到其宣告區域結尾,因此潛在作用域比宣告區域小,這是由於變數必須定以後才能使用。
一個名稱空間不會和另外一個名稱空間衝突。
名稱空間可以是全域性也可以位於另外一個名稱空間中,但不能位於程式碼塊中,因此,預設情況下在名稱空間中宣告的名稱的連結性為外部。
名稱空間定義起來很簡單:
namespace Jill
{
char fetch(){...}
...
}
使用起來也簡單,
Jill::fetch();
為了程式設計簡單,於是有了using宣告和using編譯指令,
using Jill::fetch;
之後這個函式就可以正常使用了,
using namespace Jill;
之後,這個名稱空間裡的東西就可以正常使用了。
如果就著儘量節省的原則,前者可以節省更多的程式碼長度,因為沒有用到的函式等其他名稱空間的東西沒被宣告定義。
就著安全的原則,推薦使用第一種以避免多名稱空間中的名稱衝突。
名稱空間是可以巢狀的,不過也很簡單。
名稱空間可以未命名,因為名稱空間定義之後,裡面的東西就相當於全域性變數,但是未命名就限制了此空間內的東西只能在當前檔案使用。
名稱空間及其用途
- 使用已命名的名稱空間中宣告的變數,而不是使用外部全域性變數
- 使用已命名的名稱空間中宣告的變數,而不是使用靜態全域性變數
- 僅將using作為一種將舊程式碼轉換為使用名稱空間的權宜之計
- 不要在標頭檔案中使用using編譯指令,非要新增using的話,只少要保證include的正確順序
- 匯入名稱空間時首選使用作用解析運算子或是using宣告而不是using編譯指令
- 對於using宣告,,首選將其作用域設定為區域性而不是全域性
總之,名稱空間的引入是為了管理專案。
歡迎到微信裡去當吃瓜群眾