C++內(nèi)存模型與名稱(chēng)空間概念講解
程序可分為三部分:
頭文件:包含結(jié)構(gòu)聲明和使用這些結(jié)構(gòu)的函數(shù)的原型
源代碼文件:包含與結(jié)構(gòu)有關(guān)的函數(shù)的代碼
源代碼文件:包含調(diào)用與結(jié)構(gòu)相關(guān)的函數(shù)代碼。
頭文件中常包含的內(nèi)容:
函數(shù)原型、使用#define或const定義的符號(hào)常量、結(jié)構(gòu)聲明、類(lèi)聲明、模板聲明、內(nèi)聯(lián)函數(shù)。
文件在編譯時(shí)可以解釋為翻譯單元。
1、存儲(chǔ)持續(xù)性與作用域及鏈接性
存儲(chǔ)類(lèi)別如何影響信息在文件間的共享呢?C++使用三種不同的方案來(lái)存儲(chǔ)數(shù)據(jù),這些方案的區(qū)別在于數(shù)據(jù)保留在內(nèi)存中的時(shí)間:
自動(dòng)存儲(chǔ)特性:在函數(shù)定義中聲明的變量(包括函數(shù)參數(shù))的存儲(chǔ)持續(xù)性為自動(dòng)的,他們?cè)诔绦蜷_(kāi)始執(zhí)行其所屬的函數(shù)或代碼塊時(shí)被創(chuàng)建,在執(zhí)行完函數(shù)或代碼塊時(shí),他們使用的內(nèi)存被釋放
靜態(tài)存儲(chǔ)特性:在函數(shù)定義外定義的變量和使用關(guān)鍵字static定義的變量的存儲(chǔ)持續(xù)性都為靜態(tài),他們?cè)诔绦蛘麄€(gè)運(yùn)行過(guò)程中都存在
線程存儲(chǔ)持續(xù)性:如果變量使用關(guān)鍵字thread_local聲明,其聲明周期和所屬線程一樣長(zhǎng)
動(dòng)態(tài)存儲(chǔ)特性:用new運(yùn)算符分配的內(nèi)存將一直存在,直到使用delete運(yùn)算符釋放或者程序結(jié)束。
2、作用域和鏈接
作用域描述了名稱(chēng)在文件(翻譯單元)的多大范圍內(nèi)可見(jiàn)。鏈接性描述了名稱(chēng)如何在不同單元間共享,鏈接性為外部的名稱(chēng)可在文件間共享,鏈接性為內(nèi)部的名稱(chēng)只能由一個(gè)文件中的函數(shù)共享,自動(dòng)變量的名稱(chēng)沒(méi)有鏈接性,因?yàn)樗麄儾荒芄蚕怼?/p>
3、靜態(tài)持續(xù)變量
靜態(tài)存儲(chǔ)持續(xù)性變量有三種鏈接性:外部、內(nèi)部和無(wú)鏈接性,這三種鏈接性都在整個(gè)程序執(zhí)行期間存在,它們的壽命更長(zhǎng),編譯器將分配固定的內(nèi)存。另外如果沒(méi)有顯式初始化靜態(tài)變量,編譯器將把它設(shè)置為0。
int global = 100; // 外部鏈接 static int one_file = 10; // 內(nèi)部鏈接 extern double up = 0; // 外部鏈接 int func(int n) { static int cnt = 0; // 無(wú)鏈接,只在代碼塊內(nèi)使用 return 0; }
4、靜態(tài)持續(xù)性和外部鏈接性
鏈接性為外部的變量稱(chēng)為外部變量,他們的存儲(chǔ)性為靜態(tài),作用域?yàn)檎麄€(gè)文件。
C++提供有兩種變量聲明,一種是定義聲明,它給變量分配存儲(chǔ)空間;另一種是引用聲明,它不給變量分配存儲(chǔ)空間,而是引用已有的變量。
double d; // 定義 extern int a; // 引用聲明 extern char c = 'a'; 定義聲明
如果要在多個(gè)文件中使用外部變量,只需要在一個(gè)文件中包含該變量的定義,在使用該變量的其他所有文件中,都必須使用關(guān)鍵字extern聲明它。
// a.h #pragma once int getGlobalNum() // a.cpp #include "a.h" extern int global; int getGlobalNum() { return global; } // b.cpp #include "a.h"; int global = 100; int main() { cout << getGlobalNum() << endl; // 100 getchar(); }
定義與全局變量同名的局部變量后,局部變量將隱藏全局變量。C++中提供了作用域解析運(yùn)算符(::),放在變量名前面是,該運(yùn)算符表示使用變量的全局版本。
// a.cpp #include "a.h" extern int global; int getGlobalNum() { int global = 10; return ::global; } // b.cpp #include "a.h"; int global = 100; int main() { cout << getGlobalNum() << endl; // 100 getchar(); }
5、靜態(tài)持續(xù)性與內(nèi)部鏈接性
將static限定符用于作用域?yàn)檎麄€(gè)文件的變量時(shí),該變量的鏈接性為內(nèi)部的,鏈接性為內(nèi)部的變量只能在其所屬的文件中使用。
常規(guī)外部變量具有外部鏈接性,即可以在其他文件中使用,如果要在其他文件中使用相同的名稱(chēng)來(lái)表示其他變量,需要使用static。(如果在兩個(gè)文件中有兩個(gè)相同名稱(chēng)的外部變量,那么第三個(gè)文件引用時(shí)就不能確定引用哪一個(gè))
// a.cpp extern int a = 10; // b.cpp extern int a = 30; // error static int a = 30; // OK
6、靜態(tài)存儲(chǔ)性與無(wú)鏈接性
在代碼塊中使用static時(shí),將導(dǎo)致局部變量的存儲(chǔ)持續(xù)性為靜態(tài)的,該變量在代碼塊不在活動(dòng)時(shí)仍存在。兩次函數(shù)調(diào)用之間,靜態(tài)局部變量的值將保持不變。初始化了靜態(tài)局部變量,程序只在啟動(dòng)時(shí)進(jìn)行一次初始化,以后再調(diào)用時(shí)將不會(huì)再被初始化。
void add2() { static int value = 0; cout << value++ << " "; // 0 1 2 } for (size_t i = 0; i < 3; i++) add2();
7、const
默認(rèn)情況下全局變量的鏈接性為外部的,但是const全局變量的鏈接性為內(nèi)部的,也就是說(shuō)C++中全局const定義就像使用了static說(shuō)明符一樣。因?yàn)橛性撎匦裕琧onst修飾的常量可以放在頭文件中,并且可以在多個(gè)文件中使用該頭文件(如果const聲明是外部的,根據(jù)單定義規(guī)則將出錯(cuò),即只有一個(gè)文件可以使用const聲明,其他需要用extern來(lái)提供引用聲明)。
extern const int a = 10; // 外部聲明的常量 const int b = 10; // 內(nèi)部聲明的常量
如果希望某個(gè)常量的鏈接性是外部的,可以使用extern來(lái)覆蓋默認(rèn)的內(nèi)部鏈接性。
8、函數(shù)和鏈接性
C++不允許在一個(gè)函數(shù)中定義另一個(gè)函數(shù),所以所有的函數(shù)的存儲(chǔ)持續(xù)性都自動(dòng)為靜態(tài)的,即整個(gè)程序執(zhí)行期間都存在。
默認(rèn)情況下,函數(shù)的鏈接性為外部的,可以使用extern來(lái)指出函數(shù)實(shí)在另一個(gè)文件中定義的(而不用去加頭文件);
extern int pub();
可以使用static將函數(shù)的鏈接性設(shè)置為內(nèi)部的,使之只能在一個(gè)文件中使用,必須同時(shí)在原型和定義中使用該關(guān)鍵字。
static int private(); static int private() { }
使用static修飾函數(shù)并不僅意味著該函數(shù)只在當(dāng)前文件中可見(jiàn),還意味著可以在其他文件中定義同名的函數(shù)。
// a.cpp int pub() { return 0; } // b.cpp int pub() // error { return 0; } static int pub() // OK { return 0; }
在定義靜態(tài)函數(shù)的文件中,靜態(tài)函數(shù)將覆蓋外部定義。
對(duì)于每個(gè)非內(nèi)聯(lián)函數(shù),程序只能包含一個(gè)定義(如果兩個(gè)文件中包含相同名稱(chēng)的外部函數(shù),那么第三個(gè)文件使用外部函數(shù)時(shí)將不能確定使用哪個(gè)定義)
內(nèi)聯(lián)函數(shù)不會(huì)受到單定義規(guī)則約束,所以可以放在頭文件中,這樣包含有頭文件的每個(gè)文件都有內(nèi)聯(lián)函數(shù)的定義。C++要求同一個(gè)函數(shù)的所有內(nèi)聯(lián)定義都必須相同。
9、語(yǔ)言的鏈接性
鏈接程序要求每個(gè)不同的函數(shù)都有不同的符號(hào)名。C語(yǔ)言中一個(gè)名稱(chēng)只能對(duì)應(yīng)一個(gè)函數(shù),這很容易實(shí)現(xiàn),C語(yǔ)言編譯器可能將spiff翻譯為_(kāi)spiff。但是在C++中,同一個(gè)名稱(chēng)可能對(duì)應(yīng)多個(gè)函數(shù),必須將這些函數(shù)翻譯為不同的符號(hào)名稱(chēng),可能將spiff(int)翻譯為_(kāi)spiff_i,將spiff(double)翻譯為_(kāi)spiff_d。
鏈接程序?qū)ふ遗cC++函數(shù)調(diào)用匹配的函數(shù)時(shí),使用的方法與C語(yǔ)言不同,要在C++程序中使用C庫(kù)中預(yù)編譯的函數(shù)可以在聲明時(shí)指定鏈接性說(shuō)明符,比如下面第一種指定用C語(yǔ)言的鏈接方式去鏈接spiff方法
extern "C" void spiff(int); // 使用C語(yǔ)言鏈接性 extern void spoff(int); // 默認(rèn)使用C++鏈接性 extern "C++" void spaff(int); // 顯式指定C++鏈接性
假設(shè)有一個(gè)C庫(kù)libC,里面有一個(gè)函數(shù)spiff,如果我們?cè)贑++程序中直接引用頭文件,并且調(diào)用函數(shù),那么會(huì)出現(xiàn)找不到函數(shù)定義的情況。這時(shí)候我們可以用extern "C"將頭文件包裹起來(lái),表示用C語(yǔ)言的連接方式去鏈接方法:
extern "C" { #include "a.h" }
10、命名空間
一個(gè)命名空間中的名稱(chēng)不會(huì)與另一個(gè)命名空間中的相同名稱(chēng)發(fā)生沖突,命名空間可以是全局的,也可以位于另一個(gè)命名空間中,但不能位于代碼中。默認(rèn)情況下,命名空間中聲明的名稱(chēng)的鏈接性為外部的。
除了用戶定義的命名空間外,還有一個(gè)全局命名空間,它對(duì)應(yīng)于文件及聲明區(qū)域,因此前面所說(shuō)的全局變量現(xiàn)在被描述為位于全局名稱(chēng)中間中。
namespace A { int a = 10; void printk() { } } namespace B { int a = 20; void printk() { } }
名稱(chēng)空間是開(kāi)放的,可以把名稱(chēng)加入到已有的名稱(chēng)空間中:
namespace B { void printkk(); }
C++提供using聲明和using編譯兩種機(jī)制來(lái)簡(jiǎn)化對(duì)名稱(chēng)空間中名稱(chēng)的使用:
using聲明使特定的標(biāo)識(shí)符可用,在函數(shù)外使用using聲明,可以把名稱(chēng)添加到全局名稱(chēng)空間中
using B::printkk; int main() { using B::printk; printk(); }
using編譯指令使整個(gè)名稱(chēng)空間可用,在全局聲明區(qū)域中使用using編譯指令,使得該名稱(chēng)空間中的名稱(chēng)全局可用:
using namespace B; int main() { // using namespace B; printk(); }
如果名稱(chēng)空間和聲明區(qū)域定義了相同的名稱(chēng),如果使用using聲明來(lái)導(dǎo)入,則兩個(gè)名稱(chēng)會(huì)發(fā)生沖突:
namespace B { int a = 20; } int main() { using B::a; // int a = 10; // error cout << a; getchar(); return 0; }
如果用using編譯指令將名稱(chēng)空間的名稱(chēng)導(dǎo)入,則局部版本將隱藏名稱(chēng)空間版本:
namespace B { int a = 20; } int a = 100; int main() { using namespace B; int a = 10; cout << a << " " << ::a << " " << B::a << endl; // 10 100 20 }
一般來(lái)說(shuō),使用using聲明比使用using編譯指令更安全,如果名稱(chēng)和局部名稱(chēng)發(fā)生沖突,編譯器將發(fā)出指示。using編譯導(dǎo)入所有名稱(chēng)可能包括不需要的名稱(chēng)。
命名空間的聲明可以進(jìn)行嵌套:
namespace element { namespace fire { } } using namespace element::fire;
可以在名稱(chēng)空間中使用using編譯指令和using聲明:
namespace spaceA { int a = 10; } namespace spaceB { using namespace A; }
可以給命名空間創(chuàng)建別名,來(lái)簡(jiǎn)化嵌套命名空間的使用:
namespace spaceA { namespace B { int a = 10; } } namespace spaceX = spaceA::spaceB; spaceX::a = 100;
命名空間的使用指導(dǎo)原則:
使用在已命名的名稱(chēng)空間中聲明的變量,而不是使用外部全局變量;
使用在已命名的名稱(chēng)空間中聲明的變量,而不是使用靜態(tài)全局變量;
如果開(kāi)發(fā)了一個(gè)函數(shù)庫(kù)或者一個(gè)類(lèi)庫(kù),將其放在一個(gè)命名空間中;
不要在頭文件中使用using編譯指令,如果非要使用將其放在所有預(yù)處理指令之后;
導(dǎo)入名稱(chēng)時(shí),首選使用作用域解析運(yùn)算符或using聲明的方法;
對(duì)于using聲明,首選將其作用域設(shè)置為局部而不是全局。
到此這篇關(guān)于C++內(nèi)存模型與名稱(chēng)空間概念講解的文章就介紹到這了,更多相關(guān)C++內(nèi)存模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Linux系統(tǒng)下如何使用C++解析json文件詳解
JSON(JavaScript Object Notation, JS 對(duì)象簡(jiǎn)譜) 是一種輕量級(jí)的數(shù)據(jù)交換格式。下面這篇文章主要給大家介紹了關(guān)于Linux系統(tǒng)下如何使用C++解析json文件的相關(guān)資料,需要的朋友可以參考下2021-06-06C++實(shí)現(xiàn)自定義撤銷(xiāo)重做功能的示例代碼
在使用c++做界面開(kāi)發(fā)的時(shí)候,尤其是實(shí)現(xiàn)白板功能時(shí)需要自己實(shí)現(xiàn)一套撤銷(xiāo)重做功能.如果是qt則有QUndoable對(duì)象,可以直接拿來(lái)用。但是如果是使用gdi繪圖,則可能需要自己實(shí)現(xiàn)了。本文就來(lái)用C++實(shí)現(xiàn)自定義撤銷(xiāo)重做功能,需要的可以參考一下2022-12-12深入解析C++編程中__alignof 與__uuidof運(yùn)算符的使用
這篇文章主要介紹了C++編程中__alignof 與__uuidof運(yùn)算符的使用,是C++入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2016-01-01C++ 中回調(diào)函數(shù)詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了C++ 中回調(diào)函數(shù)詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06C++小利器之std::bind參數(shù)綁定包裝器的使用詳解
從 C++11 開(kāi)始,標(biāo)準(zhǔn)庫(kù)提供了 std::bind 用于綁定函數(shù) f 和調(diào)用參數(shù),返回一個(gè)新可調(diào)用函數(shù)對(duì)象 fn,下面就跟隨小編一起深入了解一下std::bind的具體使用吧2023-12-12C語(yǔ)言實(shí)現(xiàn)銀行管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)銀行管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01從頭學(xué)習(xí)C語(yǔ)言之switch語(yǔ)句和分支嵌套
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言之switch語(yǔ)句和分支嵌套,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-01-01