static静态元素
背景
对static静态元素做一个系统的学习记录
static基本含义
在c/c++种,static都有两种基本含义,并且这两种基本含义经常是互相冲突的。
(1)在固定的地址上进行存储分配,也就是说对象在一个特殊的静态数据区上创建的,而不是每次函数调用时在堆栈上产生的。这也是静态存储的概念。
(2)对一个特定编译单位来说是局部的。因此,static控制名字的可见性,所以这个名字在这个单元或者类之外是不可见的。这也描述了连接的概念,它决定连接器将看到哪些名字。
函数内部的静态变量
通常,在函数体内部定义一个局部变量时,编译器在每次函数调用时使堆栈的指针下移一个适当的位置,为这些局部变量分配内容。如果这个变量有一个初始化表达式,那么每当程序运行到此处,初始化就被执行。
然而,有时想在两次函数调用之间保留一个变量的值,可以通过定义一个全局变量来实现,但这样一来,这个变量就不仅仅只是受到这个函数的控制。
c/c++中都准许在函数体内部定义一个static对象,这个对象存储在程序的静态数据区内,而不是在堆栈中。这个对象只在函数第一次调用时初始化一次,以后它将在两次函数调用之间保持它的值。
如果没有为一个内建类型的静态变量提供一个初始值的话,编译器也会确保在程序开始时它被初始化为0(转化为适当的类型)。
其实对静态对象初始化(与其他对象的初始化一样)可以是任意的常量表达式,常量表达式可以出现常量以及在此之前声明过的变量和函数。
注意:无论什么时候设计一个包含静态变量的函数时,都应记住多线程问题。
函数内部的静态对象
(1)静态对象的构造函数
对一般静态变量的规则同样适用于用户自定义的静态对象,而且它同样也必须有初始化操作。但是,零赋值只对内建类型有效,用户自定义类型必须用构造函数来初始化。因此,如果在定义一个静态对象时没有指定构造函数参数,这个类就必须要有默认构造函数。
示例代码如下:
运行结果如下:
(2)静态对象的析构函数
静态对象的析构函数(包括静态存储的所有对象,不仅仅是上例的局部静态对象)在程序从main()中退出时,或者标准的c库函数exit()被调用时才被调用。
多数情况下,main()函数的结尾也是调用了exit()来结束程序的。这意味着在析构函数内部使用exit()是非常危险的,因为这样导致了无穷的递归调用。
同普通对象的销毁一样,静态对象的销毁顺序也是按与初始化相反的顺序进行的。当然只有那些创建了的对象才会被销毁。开发工具会记录对象初始化的顺序和那些已被创建的对象。
全局对象总是在mian()执行之前被创建,在退出main()时被销毁。
在C++中,全局静态对象的构造函数是在main()之前调用的,所以现在有了一个在进入main()函数之前执行一段代码的简单的、可移植的方法,并且可以在退出main()之后用析构函数执行代码。在C中,如果想要做到这一点,就显得非常繁琐,我们将不得不熟悉编译器开发商的汇编语言的开始代码。
如果一个包含局部静态对象的函数从未被调用过,那么这个对象的构造函数也就不会执行,这样也就不会执行析构函数。
示例代码如下:
运行结果如下:
参考文档
《Thing in C++》