資料的儲存方案
C++中,根據資料儲存在記憶體中的時間長短,分為四種不同的方案來儲存資料。
1.自動儲存持續性
2.靜態儲存持續性
3.執行緒儲存持續性(C++11),不介紹。
4.動態儲存持續性
—————————————————————————————————————————————————————
然而在介紹這四種不同的方案之前,先介紹兩個名詞。
1.作用域:描述了物件或者函式在多大的範圍內可見。
2.連線性:連線性描述了變數名稱如何在不同單元間共享,連線性為外部的的名稱可以在檔案間共享,連線性為內部的名稱則只能在同一個檔案中的函式共享。
—————————————————————————————————————————————————————
一、自動儲存持續性
>1.自動變數(區域性變數):
在函式或者程式碼塊中宣告定義的變數儲存持續性為自動,作用域為區域性,沒有連線性,由系統棧分配記憶體。
自動變數具有隱藏(hide)同名變數的特性
[cpp] view plaincopyprint?- int main()
- {
- int number = 5;
- {
- cout << "number is: " << number << " number at: " <<&number << endl;
- int
- cout << "number is: " << number << " number at: " <<&number << endl;
- }
- return 0;
- }
int main() { int number = 5; { cout << "number is: " << number << " number at: " <<&number << endl; int number = 10; cout << "number is: " << number << " number at: " <<&number << endl; } return 0; }
兩個number雖然名稱相同,但其實作用於不同,第一個number作用域一直到return語句,而第二個number從定義處開始一直到大括號,並且在第二個number作用時,第一個number被隱藏。
同樣,自動變數還可以隱藏全域性變數,不過可以通過作用域解析運算子來特指使用全域性變數:
[cpp] view plaincopyprint?- int number = 5;
- int main()
- {
- {
- cout << "number is: " << number << " number at: " << &number << endl;
- int number = 10;
- cout << "number is: " << number << " number at: " << &number << endl;
- cout << "number is: " << ::number << " number at: " <<&(::number) << endl;
- }
- return 0;
- }
int number = 5;
int main()
{
{
cout << "number is: " << number << " number at: " << &number << endl;
int number = 10;
cout << "number is: " << number << " number at: " << &number << endl;
cout << "number is: " << ::number << " number at: " <<&(::number) << endl;
}
return 0;
}
>2.暫存器變數:特性同自動變數,但是比普通的自動變數要快,儲存在暫存器中。C++11中不在進行這種特殊處理。
二、靜態儲存持續性:
>1.全域性變數:
全域性變數也屬於靜態儲存持續性,因為有連線性,所以作用域大大擴充套件,可以在多個檔案中使用,由系統棧分配記憶體。
例如在file1中定義了一個全域性變數:
[cpp] view plaincopyprint?- int number = 5;//definition in file1
int number = 5;//definition in file1
可以通過外加extern關鍵詞的方式在其他檔案中使用:(這種方式稱為引用宣告)
[cpp]
view plaincopyprint?
- externint number;//use it in file2
extern int number;//use it in file2
注意,這裡不是
[cpp]
view plaincopyprint?
- externint number = 5;
extern int number = 5;
這種方式是對number進行了重定義,而不是引用宣告。
>2.區域性靜態變數:
同自動變數,是在函式或者程式碼塊中定義的變數,但是前邊加有static關鍵詞,這種區域性靜態變數沒有連線性,作用域也只在函式或者程式碼塊中,不同於自動變數的是,無論是否呼叫該函式都會事先建立該變數,靜態變數不是由系統棧分配記憶體的,而是一塊單獨的記憶體。同時,這也意味著即使多次呼叫函式,靜態區域性變數也有且僅有一次初始化,數值被保留。
>3.全域性靜態變數:
同全域性變數,只是在變數名稱前加static關鍵詞。全域性靜態變數連線性為內部,只在一個檔案中共享,其作用域為該檔案,由專門分配靜態變數的記憶體管理。
全域性靜態變數的作用很大,值得注意的是,通過全域性變數可以更好的理解連線性為內部是究竟是什麼意思。
情景:在多個檔案中,想使用相同名稱的但是初始值不同的全域性變數該如何?
解決方案1:
[cpp] view plaincopyprint?- int number = 5;//file1
- externint number = 10;//file2;
int number = 5;//file1
extern int number = 10;//file2;
由於引用變數給了初始值(10),又由於全域性變數連線性為外部可以在多個檔案中共享,所以這裡相當於一個重定義。正確的方法應當是使用static全域性變數,即:[cpp] view plaincopyprint?
- int number = 5;//file1
- staticint number = 10;//file2,exists in file2 only
int number = 5;//file1
static int number = 10;//file2,exists in file2 only
且明顯這兩個number的地址是不同的。
值得一提的是,如果全域性變數前加const,那麼const全域性變數的連結性為內部,相當於一個static靜態全域性變數。
內部連線性還意味著,每個檔案都有自己的一組常量,而不是所有檔案共享一組常量。每個定義都是其所屬檔案私有的,也就是能夠將常量定義放在標頭檔案中的原因。這樣,只要在兩個原始碼檔案中包含同一個標頭檔案,那麼他們就將使用同一組常量。
三、動態儲存持續性
動態儲存持續性,顧名思義就是使用動態記憶體為變數分配記憶體,在C++中體現的是用new與delete。
動態分配的記憶體,由堆來管理,它不受作用域和連線性的規則控制。
一旦被new分配記憶體後,只要不使用delete釋放將一直存在直到程式結束。
—————————————————————————————————————————————————————
通常,new負責在堆中找到一個可以滿足要求的記憶體塊,不過C++提供了一種new運算子的變體,稱為定位new運算子。
定位new運算子,允許程式設計師手動的管理分配記憶體,要使用這種特性,首先要包括標頭檔案<new>
基本的語法如下:
[cpp] view plaincopyprint?- value_point = new (buffer) value_size;
value_point = new (buffer) value_size;
從記憶體池buffer中分配一塊value_size這麼大的記憶體給value_point,其中value_point是一個指標。看一個例項,能很好的明白“手動的管理分配記憶體”的含義。 [cpp] view plaincopyprint?
- #include <iostream>
- #include <new>
- usingnamespace std;
- constint maxn = 512;
- int main()
- {
- int buffer[maxn]= {0};
- int *p1,*p2,*p3,*p4;
- p1 = newint[5];//use heap;
- p2 = new(buffer) int[5];//use buffer
- cout << "heap at: " << p1 <<" static buffer at: " << (void*)buffer << endl;
- cout << "\nThe first change" << endl;
- for(int i = 0 ; i < 5 ; ++i){
- p1[i] = i * 1 + 10;
- p2[i] = i * 2 + 10;
- }
- for(int i = 0 ; i < 5 ; ++i)
- cout << p1[i] << " at " << &p1[i] << " ; " << p2[i] << " at " << &p2[i] << endl;
- cout << "\nThe second change" << endl;
- p3 = newint[5];//use heap,differ from p1
- p4 = new(buffer) int[5];//rewrite the old data,same as p2
- for(int i = 0 ; i < 5 ; ++i){
- p3[i] = i * 3 + 10;
- p4[i] = i * 4 + 10;
- }
- for(int i = 0 ; i < 5 ; ++i)
- cout << p3[i] << " at " << &p3[i] << " ; "<< p4[i] << " at " << &p4[i] << endl;
- delete[] p1;
- delete[] p3;
- //p2,p4 can't delete
- return 0;
- }
#include <iostream>
#include <new>
using namespace std;
const int maxn = 512;
int main()
{
int buffer[maxn]= {0};
int *p1,*p2,*p3,*p4;
p1 = new int[5];//use heap;
p2 = new(buffer) int[5];//use buffer
cout << "heap at: " << p1 <<" static buffer at: " << (void*)buffer << endl;
cout << "\nThe first change" << endl;
for(int i = 0 ; i < 5 ; ++i){
p1[i] = i * 1 + 10;
p2[i] = i * 2 + 10;
}
for(int i = 0 ; i < 5 ; ++i)
cout << p1[i] << " at " << &p1[i] << " ; " << p2[i] << " at " << &p2[i] << endl;
cout << "\nThe second change" << endl;
p3 = new int[5];//use heap,differ from p1
p4 = new(buffer) int[5];//rewrite the old data,same as p2
for(int i = 0 ; i < 5 ; ++i){
p3[i] = i * 3 + 10;
p4[i] = i * 4 + 10;
}
for(int i = 0 ; i < 5 ; ++i)
cout << p3[i] << " at " << &p3[i] << " ; "<< p4[i] << " at " << &p4[i] << endl;
delete[] p1;
delete[] p3;
//p2,p4 can't delete
return 0;
}
下面是程式的執行結果:
1.p1,.p3是常規new運算子分配,需用delete釋放。p2,p4是由buffer分配,而buffer本身是一塊靜態記憶體由棧管理,因此不可以用delete釋放。
2.p2,p4是定位new運算子分配,注意實際上buffer可以為任意型別的資料分配記憶體(只要記憶體空間足夠)而不一定是int型別的,且不是在堆中。
3.p3常規運算子分配,它的地址與p1不同,而p4的地址與p2是相同的。
定位new運算子使用傳遞給它的地址,但是不跟蹤這些記憶體哪些已經被使用,也不查詢未使用的記憶體,將這些記憶體管理的任務移交給程式設計師。
可以使用以下語句進行調配:
[cpp] view plaincopyprint?- p4 = new(buffer + 5 * sizeof(int)) int[5];
p4 = new(buffer + 5 * sizeof(int)) int[5];