C 程式設計思想學習筆記 第十章 名字控制

2021-07-03 11:50:50 字數 3848 閱讀 5543

第十章 名字控制

10.1 來自c語言中的靜態元素

在c和c++的定義中,static都有兩種基本含義:

1) 在固定的位址上進行儲存分配,物件在靜態區建立,而不是每次呼叫函式時在堆疊上產生。  ===> 生存期:在每次進入定義該變數的函式時

2) 如果static變數是定義在某個檔案中,不在任何函式內,則它在該檔案中是全域性的。 ===>作用域:僅在該檔案中可用(除非用extern在別的檔案宣告)

10.1.1 函式內部的靜態變數

c++允許在函式內部定義乙個靜態的內建型別變數,程式只在第一次進入時對其初始化,之後保持之前的值。

而如果沒有為乙個內建型別的靜態變數提供乙個初始值的話,編譯器也會確保在程式開始時它被初始化為0,包括指標。

10.1.1.1 函式內部的靜態物件

與上面的靜態變數相比,不同的一點在於:零賦值只對內建型別有效,使用者自定義型別必須用建構函式來初始化。因此,如果在定義乙個靜態物件時沒有指定構造函式引數,這個類就必須有預設的建構函式。

#includeusing namespace std;

class x

~x() };

void f()

int main()

上面成的列印為:

i = 5

i = 0

this is ~x()

this is ~x()

程式控制第一次轉到物件的定義點時,才需要執行建構函式。

10.1.1.2 靜態物件的析構函式

靜態物件的析構函式:在程式從main()中退出來時,或者從標準的c庫函式exit()被呼叫時才被呼叫;而多數情況下,main函式的結尾也是呼叫了exit()來結束程式的。這一點也非常重要,這警告了:不要在類的析構函式中呼叫exit(),有可能會引起無窮的遞迴呼叫。

看下面這段**

#includeusing namespace std;

class a

~a()

};a a(1);

void f()

int main()

編譯後程式的輸出為:

this is a() i = 1

main starts

this is a() i = 2

main middle

this is a() i = 3

main ends

this is ~a() i = 2

this is ~a() i = 3

this is ~a() i = 1

這說明了以下幾點:

1、靜態物件的銷毀也是按與初始化時相反的順序進行的。

2、全域性靜態物件比main函式先初始化,main函式退出後才進行析構。參考全域性物件a,其建構函式在main函式開始之前被呼叫

3、在main函式中,即便區域性靜態物件aaa比aa晚初始化,但它並沒有比aa先調析構函式。並且他也是在main函式退出之後才呼叫的析構函式。所以有以下結論:

1) 建構函式的呼叫順序: 全域性靜態物件 >  進入main函式  > (區域性靜態物件  、臨時物件)。括號中的物件的呼叫順序要看**中呼叫的順序

2) 析構函式的呼叫順序:main函式退出 >臨時物件 > 區域性靜態物件 > 全域性靜態物件。同類的物件的析構按與建構函式的呼叫順序相反的順序呼叫。

這給了我們一種能力:再進入main()之前執行一段**,並且可以在退出main()之後用析構函式執行**。

10.1.2 控制連線

這部分主要描述了變數的程式中的可見性問題。先看兩個概念:

2、內部連線:在檔案作用域內,乙個被明確宣告為static的物件或函式的名字對翻譯單元來說是區域性於該單元的,這些名字有內部連線。

10.1.2.1 衝突問題

如果在進入main()之前定義乙個變數 int a = 0; 它等於 extern int a = 0; 說明了兩點:1、a是全域性可見的,即外部連線的。2、a的值儲存在靜態區,當main()函式結束後,它才會被釋放。

但是如果定義 static int a = 0; 僅改變上述中的第1點,他現在是本檔案中可見的,也就是說內部連線的,無論在另乙個檔案中是否宣告extern int a; 都不能引用到該變數。而它依然儲存在靜態區。

10.1.3

其他儲存型別說明符: auto --- 自動變數,可由編譯器根據上下文推導出,所以一般用不到。register -- 暫存器型別的變數。即使在程式中宣告乙個變數為register類,也不能保證它一直存在於暫存器中,這部分工作仍然由編譯器完成。

10.2 c++中的靜態成員

作用:為某個類的所有物件分配乙個單一的儲存空間

類的靜態成員擁有一塊單獨的儲存區,不管建立了多少個物件。靜態資料屬於類而不屬於某個特定的物件。

10.3.1 定義靜態資料成員的儲存

方法如下:

在類的定義處,一般是標頭檔案中,在想使之成為靜態成員的變數前加上static,如果定義乙個靜態的全域性或區域性變數一樣。這只是宣告

class a;
然後在實現檔案中定義,需要使用作用域運算子。

int a::i = 1;

10.3.1.1 靜態陣列的初始化

1) 內建型別的初始化

//in header

class values

;//in cpp

const int values::scints = "1, 2, 3, 4, 5";

char values::scchars = ;

可以不在宣告時指定陣列的大小,而利用自動計數功能確定陣列的大小。

2) 類的陣列

你可能想既然寫好了類的建構函式,利用內聯語法在宣告時定義另乙個類的靜態物件或陣列,然而這樣是不行的,必須像上面一樣寫在具有靜態變數的類的外部。

10.3.2 巢狀類和區域性類

可以把乙個靜態資料成員放在另乙個類的巢狀類中,而不能在區域性類中定義靜態資料成員。比如在乙個區域性函式內定義乙個類的靜態成員。理由很簡單,區域性函式不一定會被呼叫,其中的資料結構不一定會被初始化。而且靜態變數是分配在堆上的,而區域性變數時分配在棧上的,這也是矛盾之處。

10.3.3 靜態成員函式

其意義與靜態資料成員一樣,都是為整個類服務的,在整個程序的記憶體空間只存在乙個該函式的儲存區。

特點:靜態成員函式沒有this指標,因此它只能訪問這個類之中的其他靜態資料成員和呼叫靜態成員函式。見下面的**段

//c10: staticmemberfunctions.cpp

#include class x

int val() const

static int incr()

static int f() };

int x::j = 0;

int main()

注意在原書中,靜態變數j是private的,但是我將他改為了public,這樣方便列印操作。

列印如下:

before constructor x::j = 0

constuctor j = 5

after constructor x::j = 5

incr j = 6

f() tmp j = 6

incr j = 7

f() tmp j = 7

incr j = 8

f() tmp j = 8

這說明:1、靜態資料成員在進入主函式之前就已經被賦值

2、建構函式是可以修改靜態資料成員的值的

3、靜態成員函式可以用物件的方式呼叫,也可以用類名的方式呼叫。

由此可以引申出乙個常用的設計模式,即單例模式。整個程序的記憶體空間內只有乙份該類的記憶體映像,保證了資料的唯一性。

C 程式設計思想筆記 第十章 名字控制

三 名字空間 四 類中的靜態成員 五 c 中使用c的庫 建立名字是程式設計過程中的一項最基本的活動,當專案很大時,它會不可避免的包含大量的名字。c 允許我們對名字的產生和名字的可見性進行控制,包括名字 的儲存位置和名字的連線。本章主要介紹,static控制儲存和可見性,以及通過名字空間來控制訪問名字...

Java程式設計思想筆記 第十章

1 可以將乙個類的定義放在另乙個類的定義內部,這就是內部類。2 如果想從外部類的非靜態方法之外的任意位置建立某個內部類的物件,那麼必須顯式的指明這個物件的型別 outerclassname.innerclassname。3 內部類擁有其外部類的所有元素的訪問權。4 如果在內部類中需要生成對於外部類的...

第十章 名字控制

在c和c 中,static都有兩種基本的含義。1 在固定的位址上進行儲存分配。也就是說物件是在乙個特殊的靜態資料區上建立的,而不是每次函式呼叫時在堆疊上產生的。這也是靜態儲存的概念。2 對乙個特定的編譯單位來說是區域性的。這樣,static控制名字的可見性,所以這個名字在這個單元或者類之外是不可見的...