您的位置:首页 > 编程语言 > C语言/C++

C++编程思想笔记——命名控制

2013-06-09 09:36 225 查看
static最基本的含义是指“位置不变的某个东西”(像静电),这里则指内存中的物理位置或文件中的可见性

1.来自C语言中的静态成员

在C和C++中,static都有两种基本的含义,并且这两种含义经常是互相有冲突的:

1.在固定的地址上分配,也就是说对象是在一个特殊的静态数据区上创建的,而不是每次函数调用时在堆栈上产生的,这也是静态存储的概念

2.对一个特定的编译单位来说是本地的,这里static控制名字的可见性,所以这个名字在这个单元或类之外是不可见的。这也描述了连接的概念,它决定连接器将看到哪些名字

1.1函数内部的静态变量

通常,在函数体内定义一个变量时,编译器使得每次函数调用时堆栈的阵阵向下移一个适当的位置,为这些内部变量分配内存。如果这个变量有一个初始化表达式,那么每当程序运行到此处,初始化就被执行。
然而,有时想在两次函数调用之间保留一个变量的值,我们可以通过定一个全局变量来实现这点,但这样一来,这个变量就不仅仅受这个函数控制。C和C++都允许在函数内部创建一个static对象,这个对象将存储在程序的净他数据区中,而不是在堆栈中。这个对象只在函数第一次调用时初始化一次,以后它将在两次函数之间保持它的值。

函数体内部的静态对象
1.用户自定义类型必须用构造函数来初始化,因此,如果在定义一个静态对象时没有指定构造函数,这个类就必须有缺省的构造函数。
2.静态对象的析构函数(包括静态存储的所有对象,不仅仅是局部静态变量)在程序从main()块中退出时,或者标准的C库函数exit()被调用时才被调用。多数情况下main()函数的结尾也是调用exit()来结束程序的。这意味着在析构函数里面使用exit() 是很危险的,因为这可能会陷入一个死循环中。但如果使用标准C库中的abort()来退出程序,惊天对象的析构函数并不会被调用。
可以用atexit()来指定当前程序跳出main()时应执行的操作,这这种情况下,子啊跳出main()或调用exit()之前,用aeexit()注册的函数可以在所有对象的析构函数之前被调用。
静态对象的销毁是按它们初始化时的相反顺序进行的。当然只有那些已经被创建的对象才会被销毁。全局对象总是在main()执行之前被创建了,所以最后一条语句只对桉树局部的静态对象起作用。

全局静态对象是在进入main()函数前进行构造的,所以可以用这个办法在main()函数前执行一段简单的代码

1.2控制连接

一般情况下,在文件范围内的所有名字(既不嵌套在类或函数中的名字)对程序中的所有编译单元来说都是可见的。这就是所谓的外部链接,因为在链接时这个名字对连接器是可见的,外部的编译单元、全局变量和普通函数都有外部链接。
有时候想让一个变量下文件范围内是可见的,这样这个文件中的所有函数都可以使用它,但不想让这个文件之外的函数看到或访问该变量,或不想这个变量的名字与外部的标识符相冲突
在文件范围内,一个被明确声明为static的对象或函数的名字对编译单元(.cpp文件)来说是局部变量,这些名字有内部连接。这意味着我们可以在其他的编译单元中使用相同的名字,而不会发生名字冲突。
内部连接的好处是这个名字可以放在一个头文件中而不用担心连接时会发生冲突。那些通常放在头文件里的名字,想常量、内联函数,在缺省情况下都是内部连接(常量只有在C++中是内部连接的,C中缺省为外部链接)。连接只引用那些在链接/装载期间有地址的成员,因此类声明和局部变量并没有连接

2.C++中的静态成员

2.1定义静态数据成员的存储

类的静态数据成员定义必须出现在类的外部而且只能定义一次,通常放在源文件中。一个静态成员的初始化表达式是在一个类的范围内

静态数组的初始化

对所有的静态数据成员,我们必须提供一个单一的外部定义。这些定义必须有内部连接,所以可以放在头文件中,初始化静态数组的方法与其他集合类新的初始化一样,但不能用自动计数。在类定义结束时,编译器必须知道足够的信息来创建对象,包括所有成员的精确大小。

类中的编译时常量

用枚举型来创建一个编译时常量使枚举丧失了本来的作用,称为“enum back”

可以使用静态常量作为编译时常量在类中使用,这种方法的一个好处是任何预定义类型都可以作为一个静态常量成员,而用enum时,只能使用整形值。

嵌套类和局部类

可以很容易把一个静态数据成员子放在一个嵌套类中。然而在局部类中不能有静态数据成员。

静态成员函数

静态成员函数为类的全体服务而不是为一个类的部分对象服务,这样就不需要定义一个全局函数,减少了全局或局部名字空间的占用。静态成员函数不能访问一般的数据成员,只能访问静态数据成员,也只能调用其他的静态成员函数。通常当前对象的地址(this)是被隐含地传到被调用函数的。但一个静态成员函数没有this,所以它无法访问一般的成员函数,这样使用静态成员函数在速度上比全局函数有不少的增长。

用static关键字指定了一个类的所有对象占有相同的一块存储空间,函数可以并行使用它,这意味着一个局部变量只有一个拷贝,函数每次调用都使用它。

静态初始化的依赖因素

在一个指定的编译单元中,静态对象的初始化顺序严格按照对象在改单元中定义出现的顺序,而清楚的孙旭与初始化的顺序正好相反

在多个编译单元之间没有严格的初始化顺序,也没有办法来指定这种顺序,这可能引起不小的问题

//first file

#include <fstream.h>

ofstream out("out.txt");

另一个文件在它的初始化表达式中用到了out对象

//second file

#include <fstream.h>

extern ofstream out;

class oof {

public:

oof() { outt << "barf"; }

} OOF;

这个程序可能运行,也可能不运行,如果第二个文件先初始化,oof的构造函数依赖out的存在,而此时out还没有创建,于是引起混乱。

有三种方法解决静态数据初始化依赖引起的问题

1.不用它,避免初始化时的依赖,这是最好的解决办法

2.如果实在要用,把那些关键的静态对象的定义放在一个文件中,这样只要让他们在文件中顺序正确就可以保证他们正确的初始化

3.如果确信把静态对象放在几个编译单元中是不可避免的,这时可以用由Jerry Schwarz在创建iostream库时提供的一种技术。这一急速要求在库文件中加上一个额外的类。这个类负责库中静态对象的动态初始化

转换连接指定

如果C++中编写一个程序要用到C库,那改怎么办?如果这样声明一个C函数;

float f(int a, int b);

C++的编译器就会将这个名字变成_f_int_int之类的东西以支持哈数重载。然而C编译器编译的库一般不做这样的转换,所以它的内部名为_f,这样,连接器将无法解决我们C++对f()的调用。

C++中通过重载extern关键字来实现连接转换指定
extern “C” float f(int a, int b);
这就告诉编译器f()是C连接,这样就不会转换函数名。标准的链接指定符有“C”和“C++”两种
extern “C” {
float f(int a, int b);
double d(int a, int b);
}
或在头文件中
extern "C"{
#include "myheader.h"
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: