單定義原則杈抢、外部變量與extern
??C++有“單定義原則(One Definition Rule, ODR)”, 該規(guī)則決定了任何變量都只能有一次定義数尿。為了實(shí)現(xiàn)這種需求,C++提供了兩種變量聲明惶楼。一種是定義聲明(definition declaration),或者簡稱為定義(definition)右蹦,它給變量分配存儲(chǔ)空間;另外一種是引用聲明(referencing declaration)歼捐,或者簡稱為聲明(declaration)何陆,它不給變量分配存儲(chǔ)空間,因?yàn)樗靡延械淖兞俊?br>
??C++中引用聲明使用的關(guān)鍵字是"extern"窥岩,并且不進(jìn)行初始化甲献,否則,聲明將變成定義颂翼,編譯器會(huì)給該變量分配存儲(chǔ)空間晃洒。
??extern主要針對(duì)具有多個(gè)源文件的項(xiàng)目慨灭。如果有多個(gè)源文件需要使用到相同的全局變量,則這些變量只需要在其中的某一個(gè)源文件中定義一次球及,其它的源文件如果想使用這些外部變量氧骤,需要在聲明的時(shí)候前面加上"extern"。
//file01.cpp
extern int cats = 20; // 對(duì)cat定義
int dogs 22; // 定義dogs
int fleas; // 定義fleas
…
//file02.cpp
//use cat and dogs from file01.cpp
extern int cats; // 無需定義吃引,直接使用外部變量
extern int dogs;
extern int fleas;
…
??從上面這段代碼中可以看出筹陵,在定義變量的時(shí)候,關(guān)鍵字extern并不是必須的镊尺,但是朦佩,如果想聲明外部變量,則extern關(guān)鍵字是必不可少的庐氮。
單定義原則(ODR)≠ 不允許存在同名變量
??計(jì)算機(jī)識(shí)別變量依靠的是地址(務(wù)必記住這句話语稠,只要是地址不一樣,即使它們的名字相同弄砍,計(jì)算機(jī)也會(huì)認(rèn)為它們是不同的變量)仙畦,全局變量和靜態(tài)變量存儲(chǔ)在靜態(tài)區(qū),而函數(shù)中聲明音婶、定義的變量則存儲(chǔ)在棧區(qū)慨畸。不同函數(shù)之間聲明的同名自動(dòng)變量是彼此獨(dú)立的。但是衣式,對(duì)于每個(gè)“變量”寸士,程序中僅允許定義一次,服從ODR瞳收。
??此外碉京,如果在函數(shù)的內(nèi)部聲明、定義一個(gè)與全局變量同名同類型的自動(dòng)變量螟深,那么該變量的值會(huì)覆蓋原來的全局變量谐宙。以下程序?qū)Υ擞幸粋€(gè)較好的詮釋。
//external.cpp
// author: pengqi
// date: 2020/03/02
// reference: C++ Primer Plus P312
#include<iostream>
using namespace std;
// external warming
double warming = 1.7; // define warming
void update(double dt);
void local();
int main() {
cout << "Global warming is " << warming << endl;
update(1.1);
cout << "Global warming is " << warming << endl;
local();
cout << "Global warming is " << warming << endl;
return 0;
}
------------------
//external.cpp
// author: pengqi
// date: 2020/03/02
// reference: C++ Primer Plus P312
#include<iostream>
extern double warming; // Using the external warming
void update();
void local();
using std::cout;
void update(double dt) {
warming = dt;
}
void local() {
double warming = 0.5;
cout << "Local warming is " << warming << std::endl;
cout << "However, Global warming is " << ::warming << std::endl;
}
??程序運(yùn)行的輸出:
$ g++ external.cpp support.cpp -o extern_test
$ ./extern_test
Global warming is 1.7
Global warming is 1.1
Local warming is 0.5
However, Global warming is 1.1
Global warming is 1.1
??從上面程序的運(yùn)行結(jié)果中可以看出界弧,函數(shù)local同樣聲明了一個(gè)名為warming的變量凡蜻,這時(shí),在函數(shù)內(nèi)部垢箕,warming的值使用的是新聲明的這個(gè)划栓,原來的全局變量被隱藏了。如果想使用原來的全局變量条获,需要在變量前面加上“::”, 稱之為作用域解析運(yùn)算符忠荞。這也是C++比C語言優(yōu)越的一點(diǎn)體現(xiàn)。
全局變量 VS 局部變量
??全局變量固然有其優(yōu)勢(shì),所有的函數(shù)都可以訪問委煤,甚至是函數(shù)在調(diào)用的時(shí)候都不用傳遞參數(shù)堂油。但是,過分地使用全局變量也會(huì)使得程序變得不可靠碧绞。計(jì)算經(jīng)驗(yàn)表明府框,程序越能避免對(duì)數(shù)據(jù)進(jìn)行不必要的訪問,就越能保持?jǐn)?shù)據(jù)的完整性讥邻。C++作為一門面向?qū)ο蟮木幊陶Z言迫靖,本身就在數(shù)據(jù)隔離方面邁出了關(guān)鍵性的一步。
加static的全局變量與不加的有何區(qū)別
??如果整個(gè)程序只有一個(gè)源文件兴使,那么在全局變量前加不加static將不會(huì)對(duì)其產(chǎn)生任何影響系宜。但是如果程序由多個(gè)源文件組成,那么static就會(huì)對(duì)該全局變量的作用域產(chǎn)生影響了发魄。加上了static的全局變量蜈首,其作用域?qū)⒈幌薅ㄔ诒疚募?nèi),雖然變量可以在文件內(nèi)部全局調(diào)用欠母,但是其鏈接性將變成內(nèi)部。
??觀察下面一段代碼:
//file1
int error = 20;
...
---------------------
//file2
int error = 5;
void foo() {
cout << error;
}
??這種情況下吆寨,程序是編不過的赏淌,因?yàn)樗`反了單定義原則(ODR)。file1已經(jīng)創(chuàng)建了error作為外部變量的定義啄清,file2又試圖定義相同的變量六水。但是,如果把程序改成如下形式辣卒,則可以編譯通過:
//file1
int error = 20;
...
-----------------
//file2
static int error = 5;
void foo() {
cout << error; // user will use error defined in file2
}
const對(duì)于全局變量作用域的影響
??之前講過掷贾,如果不加static修飾的話,默認(rèn)全局變量的鏈接性是外部的荣茫。但是想帅,如果加了const后,該全局變量的鏈接性就會(huì)變成內(nèi)部的了啡莉。
const int fingers = 10;
??以上代碼等價(jià)于:
static const int finger = 10;
??如果我們希望const常量具有外部鏈接性港准,就需要在定義的時(shí)候前面加上extern,如下:
extern const int finger = 10;
??雖然對(duì)于普通的全局變量咧欣,加不加extern不影響浅缸。
函數(shù)的鏈接性(extern 與 static)
??與C語言一樣,C++不允許在一個(gè)函數(shù)中定義另外一個(gè)函數(shù)魄咕,因此所有的函數(shù)存儲(chǔ)持續(xù)性都自動(dòng)為靜態(tài)的衩椒,即整個(gè)程序執(zhí)行期間都一直存在。默認(rèn)情況下,函數(shù)的鏈接性都是外部的毛萌,即全局函數(shù)都可以在文件之間共享苟弛。實(shí)際上,可以在函數(shù)原型前面加上extern朝聋,表明該函數(shù)是在其它文件中定義的嗡午,不過這是可選的。static關(guān)鍵字同樣適用于函數(shù)冀痕,如果一個(gè)函數(shù)被static說明符修飾荔睹,則該函數(shù)只能在該文件中使用,且static必須在函數(shù)的聲明和定義中都使用言蛇。例如:
static int private(double x);
...
static int private(double x) {
...
}
??與此同時(shí)僻他,這還意味著其它文件中可以定義同名的函數(shù)。在此文件中腊尚,靜態(tài)函數(shù)會(huì)覆蓋同名的外部定義的全局函數(shù)吨拗,正如靜態(tài)變量會(huì)覆蓋全局變量一樣。