第十章 名字控制

在C和C++中,static都有兩種基本的含義。

1)在固定的地址上進行存儲分配。也就是說對象是在一個特殊的靜態數據區上創建的,而不是每次函數調用時在堆棧上產生的。這也是靜態存儲的概念。

2)對一個特定的編譯單位來說是局部的。這樣,static控制名字的可見性,所以這個名字在這個單元或者類之外是不可見的。


C和c++都允許在函數內部定義一個static對象,這個對象將存儲在程序的靜態數據區中,而不是在堆棧中。這個對象只在函數第一次調用時初始化一次,以後它將在兩次函數調用之間保持它的值。例如:

#include<iostream>
using namespace std;

char oneChar(const char* charArray = 0)
{
	static const char* s;
	if(charArray){
		s = charArray;
		return *s;
	}
	else
		return 0;
	if(*s == '\0')
		return 0;
	return *s++;
}

char* a = "abcdefghijklmn";

int main(){
	oneChar(a);
	char c;
	while((c = oneChar()) != 0)
		cout<<c<<endl;
}

如果沒有爲一個內部類型的靜態變量提供一個初始化值的話,編譯器會確保在程序開始時將它初始化爲零(轉化爲適當的類型)。但是,零賦值只對內部類型有效,用戶自定義類型必須用構造函數來初始化,因此,如果在定義一個靜態對象時沒有指定構造函數參數,這個類就必須有默認的構造函數。如:

#include<iostream>
using namespace std;


class X{
	int i;
public:
	X(int ii=0):i(ii){}
	~X(){cout<<"X::~X()"<<endl;}
};

void f(){
	static X x1(47);
	static X x2;
}

int main(){
	f();
}

在函數f()內部定義一個靜態的X類型的對象,它可以用帶參數的構造函數來初始化,也可以用默認構造函數。程序控制第一次轉到對象的定義點時,而且只有第一次時,才需要執行構造函數。

靜態對象的析構函數在程序從main中退出時,或者標準的C庫函數exit()被調用時才被調用。但是如果用標準的C庫函數abort()來退出程序,靜態對象的析構函數並不會被調用。

全局對象總是在main執行前被創建,在退出main時被銷燬。如:

#include<iostream>
using namespace std;

ofstream out("statdest.out");

class Obj{
	char c;
public:
	Obj(char cc):c(cc){
		out<<"Obj::Obj() for"<<c<<endl;
	}
	~Obj(){
		out<<"Obj::~Obj() for "<<c<<endl;
	}
};

Obj a('a');

void f()
{
	static Obj b('b');
}

void g()
{
	static Obj c('c');
}

int main(){
	out<<"inside main()"<<endl;
	f();
	out<<"leaving main()"<<endl;
}
輸出是:

Obj::Obj() for a 
inside main()
Obj::Obj() for b
leaving main()
Obj::~Obj() for b
Obj::~Obj() for a
一般情況下,在文件作用域內的所有名字對程序中的所有翻譯單元來說都是可見的,這就是所謂的外部連接,因爲在連接時這個名字對連接器來說是可見的,對單獨的翻譯單元來說,它是外部的。全局變量和普通函數都有外部連接。

在文件作用域內,一個被明確聲明爲static的對象或函數的名字對翻譯單元(也就是出現聲明的.cpp)來說是局部於單元的。這些單元有內部連接。這意味着可以在其他的翻譯單元中使用同樣的名字,而不會發生名字衝突。內部連接的一個好處是這個名字可以放在一個頭文件中而不用擔心連接時發生衝突。那些通常放在頭文件裏的名字,如常量、內聯函數、在默認情況下都是內部連接的(常量只有在c++中默認情況是內部連接的,在C中默認是外部連接的)。注意連接只引用那些在連接/裝載期間有地址的成員,因此類聲明和局部變量並不連接。

存儲類型說明符:static extern register auto

創建一個名字空間

namespace MyLib{
//Declaration
}

int main(){}

每個翻譯單元都可包含一個未命名的名字空間——可以不用標識符而只用“namespace”增加一個名字空間。

在這個空間中的名字自動地在翻譯單元內無限制地有效。但是要確保每個翻譯單元摯友一個未命名的名字空間,如果把一個局部名字放在一個未命名的名字空間中,不需要加上static說明久可以讓它們作內部連接。

在一個名字空間中引用一個名字可以採取三種方法:第一種方法是用作用域運算符,第二種方法是用using指令把所有名字引入到名字空間中,第三種方法是用using聲明一次性引用名字。

可以把一個靜態數據成員放在另一個類的嵌套類中,然而不能在局部類(在函數內部定義的類)中有靜態數據成員。

像靜態數據成員一樣,也可以創建一個靜態成員函數,它爲類的全體對象服務而不是爲一個類的特殊對象服務。靜態成員函數不能訪問一般的數據成員,而只能訪問靜態數據成員,也只能調用其他的靜態成員函數。通常,當前對象的地址(this)是被隱式地傳遞到被調用的函數的。但一個靜態成員函數沒有this,所有它無法訪問一般的成員。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章