第十章 名字控制

在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,所有它无法访问一般的成员。







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