C++檔案包含、引用、儲存類
C++檔案包含、引用、儲存類
C++檔案包含
“檔案包含”是指一個原始檔可以將另外一個原始檔的全部內容包含進來,即將另外的檔案包含到本檔案之中。C++提供了#include命令用來實現“檔案包含”的操作。如在file1.cpp中有以下#include命令:
#include ″file2.cpp″
include命令的兩種形式
在#include命令中,檔名除了可以用尖括號括起來以外,還可以用雙撇號括起來。#include命令的一般形式為:
#include <檔名>
或
#include ″檔名″
如:
#include <iostream>
或
#include ″iostream″
都是合法的。二者的區別是: 用尖括號時,系統到系統目錄中尋找要包含的檔案,如果找不到,編譯系統就給出出錯資訊。
有時被包含的檔案不一定在系統目錄中,這時應該用雙撇號形式,在雙撇號中指出檔案路徑和檔名。
如果在雙撇號中沒有給出絕對路徑,如#include ″file2.c″則預設指使用者當前目錄中的檔案。系統先在使用者當前目錄中尋找要包含的檔案,若找不到,再按標準方式查詢。如果程式中要包含的是使用者自己編寫的檔案,宜用雙撇號形式。
對於系統提供的標頭檔案,既可以用尖括號形式,也可以用雙撇號形式,都能找到被包含的檔案,但顯然用尖括號形式更直截了當,效率更高。
新的C++標準庫中的標頭檔案一般不再包括字尾.h,例如:
#include <string>
但為了使大批已有的C程式能繼續使用,許多C++編譯系統保留了C的標頭檔案,即提供兩種不同的標頭檔案,由程式設計者選用。如:
#include <iostream.h> //C形式的標頭檔案
#include <iostream> //C++形式的標頭檔案
效果基本上是一樣的。建議儘量用符合C++標準的形式,即在包含C++標頭檔案時一般不用字尾。如果使用者自己編寫標頭檔案,可以用.h為字尾。
下面給出一個例子
建立兩個檔案:myinit.cpp檔案和myA.cpp檔案。
注意,自定義的標頭檔案檔案要麼和原始碼檔案放在同一處,要麼在編譯的時候指明自定義標頭檔案路徑,這樣編譯器才能找到標頭檔案,才能編譯通過。
myinit.cpp檔案內容如下:
int a = 5, b=3 ;
cout<<"a= "<<a<<" ,b= "<<++b<<endl ;
myA.cpp檔案內容如下:
#include<iostream>
using namespace std;
int main()
{
int a = 36, b=98 ;
{
#include "myinit.cpp" //檔案包含
a++ ;
}
b= b+16 ;
cout<<"a= "<<a<<" ,b= "<<++b<<endl ;
}
編譯myA.cpp執行之,參見下圖:
C++引用
引用是C++引入的新語言特性,是C++常用的一個重要內容之一。引用變數是一個別名,也就是說,它是某個已存在變數的另一個名字。一旦把引用初始化為某個變數,就可以使用該引用名稱或變數名稱來指向變數。
引用很容易與指標混淆,它們之間有三個主要的不同:
不存在空引用。引用必須連線到一塊合法的記憶體。
一旦引用被初始化為一個物件,就不能被指向到另一個物件。指標可以在任何時候指向到另一個物件。
引用必須在建立時被初始化。指標可以在任何時間被初始化。
引用的宣告方法:
型別識別符號 &引用名=目標變數名;
int a;
int &ra=a; //定義引用ra,它是變數a的引用,即別名。在此宣告中,&讀作引用。
說明:
(1)&在此不是求地址運算,而是起標識作用。
(2)型別識別符號是指目標變數的型別。
(3)宣告引用時,必須同時對其進行初始化。
(4)引用宣告完畢後,相當於目標變數名有兩個名稱,即該目標原名稱和引用名,且不能再把該引用名作為其他變數名的別名。
ra=1;
等價於
a=1;
(5)宣告一個引用,不是新定義了一個變數,它只表示該引用名是目標變數名的一個別名,它本身不是一種資料型別,因此引用本身不佔儲存單元,系統也不給引用分配儲存單元。故:對引用求地址,就是對目標變數求地址。&ra與&a相等。
(6)不能建立陣列的引用。因為陣列是一個由若干個元素所組成的集合,所以無法建立一個數組的別名。
C++儲存類
變數具有的屬性有:資料型別(data type)、作用域((scope)、儲存類(儲存類別storage class)。
變數的作用域((scope)是從空間的角度來分析的,分為全域性變數和區域性變數。
變數的儲存期(storage duration,也稱生存期Life time) 指變數在記憶體中的存在期間。這是從變數值存在的時間角度來分析的。儲存期可以分為靜態儲存期(static storage duration)和動態儲存期(dynamic storage duration)。這是由變數的靜態儲存方式和動態儲存方式決定的。
靜態儲存方式是指在程式執行期間,系統對變數分配固定的儲存空間。動態儲存方式則是在程式執行期間,系統對變數動態地分配儲存空間。
記憶體中的供使用者使用的儲存空間的情況。這個儲存空間可以分為三部分,即:
程式區
靜態儲存區
動態儲存區
全域性變數全部存放在靜態儲存區中,在程式開始執行時給全域性變數分配儲存單元,程式執行完畢就釋放這些空間。在程式執行過程中它們佔據固定的儲存單元,而不是動態地進行分配和釋放。
在動態儲存區中存放以下資料:
函式形式引數。在呼叫函式時給形參分配儲存空間。
函式中的自動變數(未加static宣告的區域性變數)。
函式呼叫時的現場保護和返回地址等。
這些資料,在函式呼叫開始時分配動態儲存空間,函式結束時釋放這些空間。在程式執行過程中,這種分配和釋放是動態的,如果在一個程式中兩次呼叫同一函式,則要進行兩次分配和釋放,而兩次分配給此函式中區域性變數的儲存空間地址可能是不相同的。
儲存類是一種型別說明符(type specifier),該說明符控制物件(如變數、函式)的作用域和儲存期。
程式中大多數變數屬於自動變數。若未指明儲存類預設為自動(automatic)儲存類。
函式中的區域性變數,如果不用關鍵字static、extern等加以宣告,編譯系統對它們是動態地分配儲存空間的。函式的形參和在函式中定義的變數(包括在複合語句中定義的變數)都屬此類。在呼叫該函式時,系統給形參和函式中定義的變數分配儲存空間,資料儲存在動態儲存區中。資料儲存在動態儲存區中。在函式呼叫結束時就自動釋放這些空間。如果是在複合語句中定義的變數,則在變數定義時分配儲存空間,在複合語句結束時自動釋放空間。
用static宣告靜態區域性變數
有時希望函式中的區域性變數的值在函式呼叫結束後不消失而保留原值,即其佔用的儲存單元不釋放,在下一次該函式呼叫時,該變數保留上一次函式呼叫結束時的值。這時就應該指定該區域性變數為靜態區域性變數(static local variable)。換句話說,static 儲存類指示編譯器在程式的生命週期內保持區域性變數的存在,而不需要在每次它進入和離開作用域時進行建立和銷燬。因此,使用 static 修飾區域性變數可以在函式呼叫之間保持區域性變數的值。
static 修飾符也可以應用於全域性變數。當 static 修飾全域性變數時,會使變數的作用域限制在宣告它的檔案內。有時希望某些全域性變數只限於被本檔案引用,而不能被其它檔案引用,這時可以在定義全域性變數時加上static。
在 C++ 中,當 static 用在類資料成員上時,會導致僅有一個該成員的副本被類的所有物件共享。
靜態區域性變數的例
#include <iostream>
using namespace std;
int f(int a) //定義f函式,a為形參
{
int b=0;
static int c=3; //定義c為靜態區域性變數
b=b+1;
c=c+1;
return a+b+c;
}
int main( )
{
int a=2,i;
for(i=0;i<3;i++)
cout<<f(a)<<" ";
cout<<endl;
return 0;
}
執行之,參見下圖:
靜態儲存區內分配儲存單元。在程式整個執行期間都不釋放。雖然靜態區域性變數在函式呼叫結束後仍然存在,但其他函式是不能引用它的,也就是說,在其他函式中它是“不可見”的。
再給一個靜態變數的例
#include <iostream>
using namespace std;
// 函式宣告
void func(void);
static int count = 10; /* 全域性變數 */
int main()
{
while(count--)
{
func();
}
return 0;
}
// 函式定義
void func( void )
{
static int i = 5; // 區域性靜態變數
i++;
std::cout << "變數 i 為 " << i ;
std::cout << " , 變數 count 為 " << count << std::endl;
}
執行之,參見下圖:
用extern宣告外部變數
全域性變數(外部變數)是在函式的外部定義的,它的作用域為從變數的定義處開始,到本程式檔案的末尾。在此作用域內,全域性變數可以為本檔案中各個函式所引用。編譯時將全域性變數分配在靜態儲存區。
有時需要用extern來宣告全域性變數,以擴充套件全域性變數的作用域。
1) 在一個檔案內宣告全域性變數
如果外部變數不在檔案的開頭定義,其有效的作用範圍只限於定義處到檔案終了。如果在定義點之前的函式想引用該全域性變數,則應該在引用之前用關鍵字extern對該變數作外部變數宣告,表示該變數是一個將在下面定義的全域性變數。有了此宣告,就可以從宣告處起,合法地引用該全域性變數,這種宣告稱為提前引用宣告。
例、用extern對外部變數作提前引用宣告,以擴充套件程式檔案中的作用域。
#include <iostream>
using namespace std;
int max(int,int); //函式宣告
int main()
{
extern int a,b;//對全域性變數a,b作提前引用宣告
cout<<max(a,b)<<endl;
}
int a=15,b=-7;//定義全域性變數a,b
int max(int x,int y)
{
int z;
z=x>y?x:y;
return z;
}
執行之,參見下圖:
在main後面定義了全域性變數a,b,但由於全域性變數定義的位置在函式main之後,因此如果沒有程式的第5行,在main函式中是不能引用全域性變數a和b的。現在我們在main函式第2行用extern對a和b作了提前引用宣告,表示a和b是將在後面定義的變數。這樣在main函式中就可以合法地使用全域性變數a和b了。如果不作extern宣告,編譯時會出錯,系統認為a和b未經定義。一般都把全域性變數的定義放在引用它的所有函式之前,這樣可以避免在函式中多加一個extern宣告。
2) 在多檔案的程式中宣告外部變數
extern 可以用來在另一個檔案中宣告一個全域性變數或函式。如果一個程式包含兩個檔案,在兩個檔案中都要用到同一個外部變數i,不能分別在兩個檔案中各自定義一個外部變數i。正確的做法是:在任一個檔案中定義外部變數i,而在另一檔案中用extern對i作外部變數宣告,如:
extern int i;
例、本例用到兩個檔案:第一個檔案main.cpp,第二個檔案support.cpp。
main.cpp檔案內容如下:
#include <iostream>
int count ;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
support.cpp內容如下:
#include <iostream>
extern int count;
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
現在,針對上面這個的多檔案例子,演示如何使用Dev-C++進行多檔案編寫編譯過程。
1)新建空白專案。按下圖操作,新增一個空白專案:
2)為專案新增檔案。按下圖操作,為專案新增檔案
3)編譯執行。按下圖操作,編譯執行之
thread_local 儲存類
使用 thread_local 說明符宣告的變數僅可在它在其上建立的執行緒上訪問。 變數在建立執行緒時建立,並在銷燬執行緒時銷燬。 每個執行緒都有其自己的變數副本。
thread_local 說明符可以與 static 或 extern 一起使用。
可以將 thread_local 僅應用於變數宣告和定義,thread_local 不能用於函式宣告或定義。
以下演示了可以被宣告為 thread_local 的變數:
thread_local int x; // 名稱空間下的全域性變數
class X
{
static thread_local std::string s; // 類的static成員變數
};
static thread_local std::string X::s; // X::s 是需要定義的
void foo()
{
thread_local std::vector<int> v; // 本地變數
}
關於“C++儲存類” 參考:
https://docs.microsoft.com/zh-cn/cpp/cpp/storage-classes-cpp?view=vs-2019