2020 我的C++的学习之路 第九章 内存模型与名称空间
以C++ Primer Plus為參考書籍,自身歸納知識點,加深記憶。
內存模型與名稱空間
- 存儲持續性
- 作用域與鏈接
- 自動存儲持續性
- 靜態持續變量
- 靜態持續性、外部鏈接性
- 靜態持續性、內部鏈接性
- 靜態持續性、無鏈接性
- 說明符和限定符
- 存儲說明符
- cv-限定符
- 定位new運算符
- 名稱空間
- using聲明與using編譯指令
- 名稱空間的其他特性
- 未命名的名稱空間
存儲持續性
①自動存儲持續性:在函數定義中聲明的變量的存儲持續性為自動,在程序開始執行所屬的函數或代碼塊 時被創建,在執行完函數或代碼塊時,它們的內存被釋放。
②靜態存儲持續性:在函數定義外定義的變量和使用關鍵字static定義的變量的存儲性為靜態,在程序運行的整個運行過程都存在
③動態存儲持續性:用new運算符分配的內存一直都在,直到delete或者程序結束為止,這種存儲持續性為動態,有時被稱為自由存儲或者堆
④線程存儲持續性(C++11):多核處理器中,讓程序能狗將計算放在可并行處理的不同線程中,如果變量是使用關鍵字 thread_local聲明的,則生命周期與所屬的線程一樣長
作用域與鏈接
作用域(scope)描述了名稱在文件的多大范圍內可見
鏈接性(linkage)描述了名稱如何在不同單元間共享
自動存儲持續性
①在默認情況下,函數中聲明的函數參數和變量的存儲持續性都為自動,局部作用域,無鏈接性。
int main() {int n = 5;{int n =10;cout<<n;//n=10}cout<<n;//n=10 }兩個n都是局部變量,各自在局部作用域中作用,內部的n=10會隱藏外部n=5的定義,當程序離開內部代碼塊時,原來的定義又重新可見。
②自動變量被存儲在棧中,后進先出,即最后加入到棧中的變量首先被彈出。
③atuo關鍵字在C語言中用于顯式地指出變量為自動存儲,但在C++11標準下,atuo改為自動類型推斷
④register關鍵字在C語言中用于寄存器存儲自動變量以提高訪問速度,但在C++11標準下,register用于顯式指出變量為自動存儲
靜態持續變量
靜態存儲持續性變量有三種鏈接性:外部鏈接(可跨文件訪問),內部鏈接(當前文件訪問),無鏈接(當前函數或代碼塊訪問),這三種鏈接性在程序執行期間存在,編譯器將分配固定的內存存儲所有的靜態變量,而不是棧。倘若沒有顯式的初始化靜態變量,則編譯器默認為0。
//a.cpp int global = 100;//靜態全局變量,外部鏈接的靜態變量 static int n = 90;//內部鏈接的靜態變量 int main() {... }void fun1() {static int cnt = 5;//無鏈接的靜態變量int sa = 0;//自動變量 }外部鏈接性靜態持續變量:必須在代碼塊外面聲明;
內部鏈接性靜態持續變量:必須在代碼塊外面聲明,并用static限定
無鏈接性靜態持續變量:必須在代碼塊內部聲明,并用static限定
靜態持續性、外部鏈接性
倘若存在另外一個文件b.cpp使用a.cpp中的global變量,那么需要在b.cpp中使用關鍵字extern且不對global進行定義。
// external.cpp -- external variable // compile with support.cpp #include <iostream> // external variable double warming = 0.3; // warming defined// function prototypes void update(double dt); void local();int main() // uses global variable {using namespace std;cout << "Global warming is " << warming << " degrees.\n";//0.3update(0.1); // call function to change warming//0.4cout << "Global warming is " << warming << " degrees.\n";//0.4local(); // call function with local warming//0.8 0.4cout << "Global warming is " << warming << " degrees.\n";//0.4// cin.get();return 0; } // support.cpp -- use external variable // compile with external.cpp #include <iostream> extern double warming; // use warming from another file// function prototypes void update(double dt); void local();using std::cout; void update(double dt) // modifies global variable {extern double warming; // optional redeclaration,此處可以省略聲明,因為上部聲明過一次warming += dt; // uses global warmingcout << "Updating global warming to " << warming;cout << " degrees.\n"; }void local() // uses local variable {double warming = 0.8; // new variable hides external onecout << "Local warming = " << warming << " degrees.\n";// Access global variable with the// scope resolution operatorcout << "But global warming = " << ::warming;//::作用域解析運算符,放在變量名前面會使用全局版本,故此處應是0.4cout << " degrees.\n"; }靜態持續性、內部鏈接性
// twofile1.cpp -- variables with external and internal linkage #include <iostream> // to be compiled with two file2.cpp int tom = 3; // external variable definition int dick = 30; // external variable definition static int harry = 300; // static, internal linkage // function prototype void remote_access();int main() {using namespace std;cout << "main() reports the following addresses:\n";cout << &tom << " = &tom, " << &dick << " = &dick, ";cout << &harry << " = &harry\n";remote_access();// cin.get();return 0; } // twofile2.cpp -- variables with internal and external linkage #include <iostream> extern int tom; // tom defined elsewhere static int dick = 10; // overrides external dick//覆蓋上部的dick變量,此處為靜態內部鏈接變量 int harry = 200; // external variable definition,// no conflict with twofile1 harryvoid remote_access() {using namespace std;cout << "remote_access() reports the following addresses:\n";cout << &tom << " = &tom, " << &dick << " = &dick, ";cout << &harry << " = &harry\n"; }靜態持續性、無鏈接性
無鏈接性的靜態變量用關鍵詞static在代碼塊中聲明,即使該代碼塊不處于活動狀態時,該變量一直存在,并且只在啟動一次時進行初始化,以后再調用函數時,該變量不會被初始化
// static.cpp -- using a static local variable #include <iostream> // constants const int ArSize = 10;// function prototype void strcount(const char * str);int main() {using namespace std;char input[ArSize];char next;cout << "Enter a line:\n";cin.get(input, ArSize);while (cin){cin.get(next);while (next != '\n') // string didn't fit!cin.get(next); // dispose of remainderstrcount(input);cout << "Enter next line (empty line to quit):\n";cin.get(input, ArSize);}cout << "Bye\n"; // code to keep window open for MSVC++ /* cin.clear();while (cin.get() != '\n')continue;cin.get(); */return 0; }void strcount(const char * str) {using namespace std;static int total = 0; // static local variableint count = 0; // automatic local variablecout << "\"" << str <<"\" contains ";while (*str++) // go to end of stringcount++;total += count;cout << count << " characters\n";cout << total << " characters total\n"; }count每次調用都會被初始化為0,而total僅在第一次調用才會被初始化為0,每次調用后都會被存儲,從而達到計數累加的效果。
說明符和限定符
存儲說明符
①auto(C++11不再是說明符)
②register
③static
④extern
⑤thread_local
⑥mutable(即使結構或類變量為const,但用mutable可以使其聲明的成員被修改)
同一個聲明中不能使用多個說明符,但thread_local除外,可與static或extern結合使用
cv-限定符
①const
②volatile
關鍵詞volatile表明,即使程序代碼沒有對內存單元進行修改,其值也可能會發生變化。
在C++中,倘若對全局變量(靜態持續性,外部鏈接)加以const限定,可將該變量的鏈接性改為內部鏈接性,也就是說在C++中,全局變量const限定相當于static存儲說明。
定位new運算符
通常new負責在堆中找到一個滿足要求的內存塊,而定位new運算符則可以指定要使用的位置
double *ptr = new double[5];//動態分配內存 struct chaff {char drss[20];int slag; }char buffer[500]; chaff *pch; pch = new (buffer)chaff;//將結構chaff放入指定的buffer內存當中,并分配合適的內存大小 // newplace.cpp -- using placement new #include <iostream> #include <new> // for placement new const int BUF = 512; const int N = 5; char buffer[BUF]; // chunk of memory int main() {using namespace std;double *pd1, *pd2;int i;cout << "Calling new and placement new:\n";pd1 = new double[N]; // use heappd2 = new (buffer) double[N]; // use buffer arrayfor (i = 0; i < N; i++)pd2[i] = pd1[i] = 1000 + 20.0 * i;cout << "Memory addresses:\n" << " heap: " << pd1<< " static: " << (void *) buffer <<endl;cout << "Memory contents:\n";for (i = 0; i < N; i++){cout << pd1[i] << " at " << &pd1[i] << "; ";cout << pd2[i] << " at " << &pd2[i] << endl;}cout << "\nCalling new and placement new a second time:\n";double *pd3, *pd4;pd3= new double[N]; // find new addresspd4 = new (buffer) double[N]; // overwrite old datafor (i = 0; i < N; i++)pd4[i] = pd3[i] = 1000 + 40.0 * i;cout << "Memory contents:\n";for (i = 0; i < N; i++){cout << pd3[i] << " at " << &pd3[i] << "; ";//pd[3]的地址與pa[1]的地址不一樣,重新分配了一塊新的地址cout << pd4[i] << " at " << &pd4[i] << endl;//pd[4]的地址與pa[2]的地址一樣}cout << "\nCalling new and placement new a third time:\n";delete [] pd1;pd1= new double[N];pd2 = new (buffer + N * sizeof(double)) double[N]; for (i = 0; i < N; i++)pd2[i] = pd1[i] = 1000 + 60.0 * i;cout << "Memory contents:\n";for (i = 0; i < N; i++){cout << pd1[i] << " at " << &pd1[i] << "; ";cout << pd2[i] << " at " << &pd2[i] << endl;}delete [] pd1;//buffer指定的是靜態內存,而delete只能用于指向的堆內存,因此不能用deletedelete [] pd3;// cin.get();return 0; }名稱空間
名稱空間可以是全局的,也可以是位于另一個名稱空間中,但不能處于代碼塊中,因此在默認情況下,在名稱空間中聲明的名稱的鏈接性是外部的。
namespace Jack {double pail;void fetch();int pal;struct Well{...}; } namespace Jill {double bucket(double n){...};double fetch;int pal;struct Hill{...} }任何名稱空間中的名稱都不會與其他名稱空間中的名稱發生沖突,因此Jack中的fetch與Jill中的fetch共存,Jill中的Hill可以與外部的Hill共存。
名稱空間是開放的,可以把名稱加入到已有的名稱空間中
利用作用域解析運算符::來訪問名稱空間中的名稱,使用名稱空間限定該名稱:
Jack::pail = 12.34;//賦值 Jill::Hill mole;//結構 Jack::fetch();//函數調用在名稱空間中聲明的函數名的作用域為整個名稱空間,因此聲明和定義必須位于同一個名稱空間中。
using聲明與using編譯指令
using聲明:
using Jill::fetch;using編譯指令:
using namespace Jill;using聲明只是讓名稱空間中的一個名稱可用;using編譯指令則是可以使用名稱空間中的所有名稱,可以認為using編譯指令中包含了所有名稱的using聲明。
名稱空間的其他特性
可以將名稱空間聲明進行嵌套:
namespace elements {namespace fire{int flame;...}float water; }倘若要使用fire內部的名稱,則using namespace elements::fire;
另外名稱空間中也可以使用using編譯指令和using聲明:
倘若要訪問fetch,因此可以這樣myth::fetch;,也可以Jill::fetch;
未命名的名稱空間
namespace {int count; }未命名的名稱空間相當于提供了內部鏈接性的靜態變量。
總結
以上是生活随笔為你收集整理的2020 我的C++的学习之路 第九章 内存模型与名称空间的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020 我的C++学习之路 C++Pr
- 下一篇: 2020 我的C++学习之路 C++Pr