儲存持續性、作用域和連結性之二
除了執行緒儲存持續性,C++使用三種不同的方案來儲存資料,這些方案的區別就在於資料保留在記憶體中的時間。
- 自動儲存持續性:在函式定義中宣告的變數(包括函式引數)的儲存持續性是自動的。它們在程式開始執行其所屬的函式或程式碼塊時被建立,在執行完函式或程式碼塊時,它們使用的記憶體被釋放。C++有兩種儲存持續性為自動的變數。
- 靜態儲存持續性:在函式定義外定義的變數和使用關鍵字
static
定義的變數的儲存持續性為靜態。它們在程式整個執行過程中都存在。C++有三種儲存持續性為靜態的變數。 - 動態儲存持續性:用
new
運算子分配的記憶體將一直存在,直到使用delete
運算子將其釋放或程式結束為止。這種記憶體的持續性為動態,有時也被成為自由儲存(free store)和堆(heap)。
動態儲存持續性
使用C++運算子new
(或C函式malloc()
)分配的記憶體,被成為動態記憶體。動態記憶體由運算子new
和delete
控制,不是由作用域和連結性規則控制。因此,可以在一個函式中動態分配記憶體,而在另一個函式中將其釋放。其分配和釋放的順序取決於new
和delete
在何時以何種方式被使用。
雖然儲存方案概念不適用於動態記憶體,但適用於用來跟蹤動態記憶體的自動和靜態指標變數。例如,假設在一個函式中包含下面的語句:
float * p_fees = new float [20];
由new
分配的80個位元組(假設float 為4個位元組)的記憶體將一直保留在記憶體中,直到使用delete
p_fees
指標將消失。如果希望另一個函式能夠使用這80個位元組中的內容,則必須將其地址傳遞或返回給該函式。另一方面,如果將p_fees 的連結性宣告為外部的,則檔案中位於該聲明後面的所有函式都可以使用它。另外,通過在另一個檔案中使用下述宣告,便可在其中使用該指標:
extern float * p_fees;
使用new運算子初始化
如果要為內建的標量型別(如 int
或double
)分配儲存空間並初始化,可在型別名後面加上初始值,並將其用括號括起:
int *pi = new int (6); // *pi set to 6 double * pd = new double (99.99); // *pd set to 99.99
然而,要初始化常規結構或陣列,需要使用大括號的列表初始化,這要求編譯器支援C++11。C++11允許您這樣做:
struct where { double x; double y; double z; };
where * one = new where {2.5,5.3,7.2}; // C++11
int * ar = new int [4] {2,4,6,7}; // C++11
在C++11中,還可將列表初始化用於單值變數:
int *pi = new int {6}; // *pi set to 6
double * pd = new double {99.99}; // *pd set to 99.99
new失敗時
new
可能找不到請求的記憶體量。在最初的10年中,C++在這種情況下讓 new返回空指標,但現在將引發異常std:.bad_alloc。
定位new運算子
通常,new
負責在堆(heap)中找到一個足以能夠滿足要求的記憶體塊。new
運算子還有另一種變體,被稱為定位(placement) new運算子,它讓您能夠指定要使用的位置。程式設計師可能使用這種特性來設定其記憶體管理規程、處理需要通過特定地址進行訪問的硬體或在特定位置建立物件。即定位new運算子可以在指定地址建立物件、將指定記憶體分配給指定物件。
#include <new>
struct chaff
{
char dross[20];
int slag;
};
char buffer1[50]
char buffer2[500];
int main()
{
chaff *p1, *p2;
int *p3, *p4;
// 首先, new的常規形式
p1 = new chaff; // 放置結構體到堆中
p3 = new int[20]; // 放置整型陣列到堆中
// 然後, 兩種形式的定位new, placement new
p2 = new (buffer1) chaff; // 放置結構體放到 buffer1 中
p4 = new (buffer2) int[20]; // 放置整型陣列到 buffer2 中
...
}
注意,buffer1
和buffer2
都位於delete
的管轄區域之外,不能使用delete
釋放記憶體塊。另一方面,如果buffer
是使用常規new
運算子建立的,便可以使用常規delete
運算子來釋放整個記憶體塊。
定位new運算子的另一種用法是,將其與初始化結合使用,從而將資訊放在特定的硬體地址處。
定位new的其他形式
int * pi = new int; // invokes new(sizeof (int))
int * p2 = new(buffer) int; // invokes new(sizeof(int),buffer)
int * p3 = new(buffer) int [40]; // invokes new(40*sizeof(int), buffer)