C++ Primer Plus 筆記第九章
內存模型與名稱空間
9.1 單獨編譯
C++ 允許鼓勵將組件函數放在獨立的文件中,可以單獨編譯這些文件,然後將他們鏈接成可執行程序;
例程:
頭文件: coordin.h
1 #ifndef COORDIN_H_ 2 #define COORDIN_H_ 3 using namespace std; 4 5 struct polar { 6 double distance; 7 double angle; 8 }; 9 10 struct rect { 11 double x; 12 double y; 13 };14 polar rect_to_polar(rect xypos); 15 void show_polar(polar dapos); 16 17 #endif
源文件: file1.cpp
1 #include <iostream> 2 #include "coordin.h" 3 4 int main() 5 { 6 rect rplace; 7 polar pplace; 8 9 cout << "Enter the x and y values: "; 10 while (cin >> rplace.x >> rplace.y)11 { 12 pplace = rect_to_polar(rplace); 13 show_polar(pplace); 14 cout << "Next two number (q to quit): "; 15 } 16 cout << "Bye!\n"; 17 return 0; 18 }
源文件: file2.cpp
1 #include<iostream> 2 #include<cmath> 3 4 #include "coordin.h" 5 6 polar rect_to_polar(rect xypos) 7 { 8 polar answer; 9 answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y); 10 answer.angle = atan2(xypos.y, xypos.x); 11 return answer; 12 } 13 14 void show_polar(polar dapos) 15 { 16 const double Rad_to_deg = 57.29577951; 17 18 cout << "distance = " << dapos.distance; 19 cout << ", angle = " << dapos.angle * Rad_to_deg; 20 cout << " degrees\n"; 21 }
將這兩個源代碼文件和新的頭文件一起進行編譯和鏈接,將生成一個可執行程序
程序文件:
1. 頭文件: 包含結構聲明和使用這些結構的函數原型;
2. 源代碼文件: 包含與結構有關的函數的代碼;(函數定義)
3. 源代碼文件: 包含調用與結構相關的函數代碼(main 和其他使用這些函數的函數)
頭文件管理:
在同一個文件中只能將同一個頭文件包含一次,為避免多次包含同一個頭文件:
使用基於預處理器編譯指令 #ifndef (即 if not defined):
#ifndef COORDIN_H_
. . .
#endif
#define COORDIN_H_: 完成名稱定義
頭文件中常包含的內容:
1. 函數原型
2. 使用#define 或 const 定義的符號常量
3. 結構聲明
4. 類聲明
5. 模板聲明
6. 內聯函數
9.2 存儲持續性、作用域和鏈接性
C++ 使用3種不同的方案來存儲數據,區別在於數據保留在內存中的時間:
1. 自動存儲持續性:
在函數定義中聲明的變量(包括函數參數)的存儲持續性為自動的;
在程序開始執行所屬的函數代碼塊時被創建,在執行完函數或代碼塊時,被釋放;
C++ 有兩種存儲持續性為自動的變量
2. 靜態存儲持續性:
在函數定義外定義的變量和使用的關鍵字 static 定義的變量的存儲持續性都為靜態;
靜態變量在程序整個運行過程都存在;
C++有3種存儲持續性為靜態的變量
3. 動態存儲持續性:
用 new 操作符分配的內存將一直存在,知道使用 delete 操作符將其釋放或程序結束為止;
這種內存的存儲持續性為動態,有時又稱自由存儲
9.2.1 作用域和鏈接
作用域描述了名稱在文件的多大範圍內可見;
鏈接性描述了名稱如何在不同單元間共享;
9.2.2 自動存儲持續性
默認情況下,在函數中聲明的函數參數和變量的存儲持續性為自動,作用域為局部,沒有鏈接性;
函數開始執行變量所屬的代碼塊時,將為其分配內存,當函數結束時,變量消失(作用域起點為聲明的位置);
可以使用C++(和C)關鍵字 auto 來顯示地指出存儲類別:
由於關鍵字 auto 用於默認狀態下為自動的變量,因此程序員幾乎不使用它
可以使用任何在聲明時其值為已知的表達式來初始化自動變量;
自動變量的管理采用堆棧,自動變量的數目隨函數的開始和結束而增減(後進先出);
C++也支持使用 register 關鍵字來聲明局部變量,寄存器變量是另一種形式的自動變量:
CPU 訪問寄存器中的值的速度比訪問堆棧中內存快;
聲明寄存器變量:register int count_fast // request for a register variable
一般 register 變量說明符指出變量將被頻繁的使用
9.2.3 靜態持續變量
C++ 也為靜態存儲持續性變量提供了 3 種鏈接性:外部鏈接性、內部鏈接性和無鏈接性;
靜態變量在整個程序執行期間一直存在;
如果沒有顯示的初始化靜態變量,編譯器將它設置為 0;
要創建鏈接性為外部的靜態持續變量,必須在代碼塊的外面聲明它;
要創建鏈接性為內部的靜態持續變量,必須在代碼塊的外面聲明它,並使用 static 限定符;
要創建沒有鏈接性的靜態持續變量,必須在代碼塊內聲明它,並使用 static 限定符
五種變量存儲方式
存儲描述 | 持續性 | 作用域 | 鏈接性 | 如何聲明 |
自動 | 自動 | 代碼塊 | 無 | 在代碼塊中(可以使用關鍵字 auto) |
寄存器 | 自動 | 代碼塊 | 無 | 在代碼塊中,使用關鍵字 register |
靜態,無鏈接性 | 靜態 | 代碼塊 | 無 | 在代碼塊中,使用關鍵字 static |
靜態,外部鏈接性 | 靜態 | 文件 | 外部 | 在函數外 |
靜態,內部鏈接性 | 靜態 | 文件 | 內部 | 在函數外,使用關鍵字 static |
1. 靜態持續性、外部鏈接性
double warming = 0.3; // 定義聲明,給變量分配存儲空間,並初始化
extren double warming; // 引用聲明,不給變量分配存儲空間,引用已有變量,聲明指出使用外部定義的變量 warming
定義與全局變量同名的局部變量後,局部變量將隱藏全局變量;
C++ 提供了作用域解析操作符(::),當放在變量名稱前面時,操作符表示使用變量的全局版本(:: warming)
2. 靜態持續性、內部鏈接性
將 static 限定符用於作用域為整個文件的變量時,該變量的鏈接性是為內部的;
鏈接性為內部的變量只能在其所屬的文件中使用;
對於外部鏈接性變量,有且只有一個文件包含了該變量的外部定義,其他文件使用該變量,必須在引用聲明中使用關鍵字 extern;
應使用外部變量在多文件程序的不同部分之間共享數據;
應使用鏈接性為內部的靜態變量在同一個文件中的多個函數之間共享(名稱空間提供了另外一種共享數據的方法)
3. 靜態存儲持續性、無鏈接性
將 static 限定符用於在代碼塊中定義的變量,將導致局部變量的存儲持續性為靜態的;
該變量只在改代碼塊中可用,但它在該代碼塊不處於活動狀態時任然存在:
兩次函數調用之間,靜態變量的值將保持不變;
如果初始化了靜態局部變量,程序只在啟動時進行一次初始化(初始化一次)
例程:
1 #include<iostream> 2 using namespace std; 3 const int ArSize = 10; 4 void strcount(const char * str); 5 6 int main() 7 { 8 char input[ArSize]; 9 char next; 10 cout << "Enter a line: \n"; 11 cin.get(input, ArSize); 12 13 while (cin) 14 { 15 /*cin.get(next); 16 while (next != ‘\n‘) 17 cin.get(next); */ 18 while (cin.get() != ‘\n‘) 19 continue; 20 strcount(input); 21 cout << "Enter next line(empty line to quit): \n"; 22 cin.get(input, ArSize); 23 } 24 cout << "Bye\n"; 25 return 0; 26 } 27 28 void strcount(const char * str) 29 { 30 static int total = 0; 31 int count = 0; 32 33 cout << "\"" << str << "\" contains "; 34 while (*str++) 35 count++; 36 total += count; 37 cout << count << " characters\n"; 38 cout << total << " characters total\n"; 39 }
自動變量 count 被重置為0;
靜態變量 total 只在程序運行時被設置為0,以後調用函數時不再進行初始化,且兩次調用期間其值保持不變
9.2.4 說明符和限定符
存儲說明符:
1. auto 自動變量
2. register 寄存器存儲類型
3. static 被用在整個文件的聲明中,表示內部鏈接性;被用於局部聲明中,表示局部變量的存儲類型為靜態的
4. extern 引用聲明,聲明引用在其他地方定義的變量
5. mutable 當結構變量為 const ,用 mutable 說明的成員也可以被修改
再談 const:
const 全局變量的鏈接性為內部的:
const int fingers = 10 // same as static const int fingers
可以把常量放在頭文件中,同一工程下的多個文件使用該頭文件後,可以得到常量定義
9.2.5 函數鏈接性
所有函數的存儲持續性都自動為靜態的,即在整個程序執行期間都一直存在;
默認情況下,函數鏈接性為外部的,可以在文件間共享;
可以使用 static 將函數鏈接性設置為內部的,使之只能在一個文件中使用;
C++ 在哪裏查找函數:
如果函數原型指出函數是靜態的,則編譯器只在文件中查找函數定義;
否則,編譯器將在所有文件中查找
9.4 名稱空間
C++ 提供了名稱空間工具,以便更好地控制名稱的作用域:
namespace Tack {
double pail;
void fetch ( );
int pal;
struct Well { . . . };
}
除了用戶定義的名稱空間外,還存在另一個名稱空間——全局名稱空間,對應於文件級聲明區域(全局變量);
名稱空間是開放的(open):
可以把名稱加入到已有名稱空間中;
可以在文件後面再次引用 Tack 名稱空間來提供函數代碼:
namespace Tack {
void fetch ()
{
. . .
}
}
包含名稱空間的名稱(如 Tack :: pail)被稱為限定的名稱;
using 聲明和 using 編譯指令
using 聲明使特定的標識符可用:
using Jill :: fetch;
在函數內部聲明將名稱添加到局部聲明區域;
在函數外面聲明將名稱添加到全局名稱空間中
using 編譯指令使整個名稱空間可用:
using namespace Jill;
使得所有名稱空間的名稱全局可用
名稱空間可以進行嵌套;
未命名的名稱空間相當於鏈接性為內部的靜態變量(C++不贊成在名稱空間和全局作用域中使用關鍵字 static)
9.4.4 名稱空間及其前途
1. 使用已命名的名稱空間中聲明的變量,而不是使用外部全局變量;
2. 使用已命名的名稱空間中聲明的變量,而不是使用靜態全局變量;
3. 如果開發了一個函數庫或者類庫,將其放在一個名稱空間中;
4. 不要在函數頭使用 using 編譯指令;
5. 導入名稱時首先使用作用域解析操作符或 using 聲明的方法;
C++ Primer Plus 筆記第九章