首页 > 代码库 > C++ 静态存储周期(static storage duration)

C++ 静态存储周期(static storage duration)

拥有静态存储周期(static storage duration)的对象将会被一直保存到程序结束。


声明

存储类型说明符(static)用于声明对象拥有静态存储期(static storage duration)。

  • 存储类型说明符(static)只可以用于对象,函数和匿名联合体。
  • 块作用域中不能声明静态函数,也不能声明静态函数参数。
    void func(static int tag){
    	cout<<tag<<endl;
    	{
    		static void nonStaticFunc(int x); // error!
    	}
    };
    而这说明在其他作用域中声明静态函数和静态函数参数都是合法的,因此下面的程序是合法的。
    #include<iostream>
    static void func(static int tag){
    	cout<<tag<<endl;
    };
    int main(int argc, char *argv[]){	
    	func(23);	
    }
  • 关键字 static 可以用于将类的数据成员或函数成员被声明为静态存储期,这些成员被称为类的静态成员(static member)。
    • 类X的静态成员可以直接使用 qualified-id 表达式X::s来访问而不必使用点“.”或-箭头“->”来访问。 当使用点“.”或-箭头“->”来访问会先验证对象表达式(A static member may be referred to using the class member access syntax, in which case the object-expression is evaluated.)
      #include<iostream>
      using namespace std;
      
      class process {
      public:
      	static char* reschedule(){return "World!";};
      };
      process& g(){
      	process a;
      	cout<<"Hello ";
      	return a;
      };
      int main(int argc, char *argv[]){	
      	cout<<process::reschedule()<<endl;	// OK: no object necessary "World!"
      	cout<<g().reschedule()<<endl;		// g() is called, "Hello World!"
      }
    • 静态成员可以在其类中或其子类中直接使用qualified-id 表达式来访问。(In this case, the static member is referred to as if a qualified-id expression was used, with the nested-name-specifier of the qualified-id naming the class scope from which the static member is referenced.)
      #include
      using namespace std;
      
      char* g(){return "g()";};
      char* k(){return "k()";};
      struct X {
      	static char* g(){return "X::g()";};
      	static char* h(){return "X::h()";};
      	static char* w(){return "X::w()";};
      };
      struct Y : X {
      	static char* g(){return "Y::g()";};
      	static char* w(){return "Y::w()";};
      	static char *i,*j,*p,*q;
      };
      char* Y::i = g();
      char* Y::j = h();
      char* Y::p = X::w();
      char* Y::q = k();
      int main(int argc, char *argv[]){	
      	cout<<Y::i<<endl;	// output Y::g()
      	cout<<Y::j<<endl;	// output X::h()
      	cout<<Y::p<<endl;	// output X::w()	
      	cout<<Y::q<<endl;	// output k()	
      }
    • If an unqualified-id is used in the definition of a static member following the member’s declarator-id, and name lookup finds that the unqualified-id refers to a static member, enumerator, or nested type of the member’s class (or of a base class of the member’s class), the unqualified-id is transformed into a qualified-id expression in which the nested-name-specifier names the class scope from which the member is referenced. 
    • 类静态成员的定义不能使用该类或其基类的非静态成员。
      struct X{
      	static int i;
      	int j;
      };
      int X::i=sizeof(j);	// error
    • The definition of a static member may only refer to these members to form pointer to members or with the class member access syntax.
    • 静态成员也同样地遵守类的访问规则。
    • 静态成员函数中不能使用this对象。
    • 静态成员函数不能为虚函数(virtual function),也不可以声明为const, volatile 或 const volatile.
    • 拥有相同名字和相同参数类型的成员函数无法被重载,如果它们当中的人员一个被声明为静态成员函数。
      class X{
      	static void f();
      	void f();					// ill-formed
      	void f() const;				// ill-formed
      	void f() const volatile;	// ill-formed
      	void g();
      	void g() const;				// OK: no static g
      	void g() const volatile;	// OK: no static g
      };
      
    • 类的静态数据成员不是该类子对象,该类的所有对象共享该静态数据成员;因此在集合初始化中不考虑静态数据成员
      #include<iostream>
      using namespace std;
      
      struct A {
      	int i;
      	static int j;
      	int k;
      }a={1,2};
      int main(int argc, char *argv[]){
      	cout<<a.i<<endl;	// 1
      	cout<<a.k<<endl;	// 2
      	cout<<a.j<<endl;	// error!
      }
    • 类定义中的静态数据成员的声明不是其定义。静态数据成员应该在包含其类定义的名字空间中定义。静态数据成员定义的初始化表达式的范围为包含该静态数据成员的类。
      #include
      using namespace std;
      
      struct Outer{
      	static char* x;
      	struct Inner{
      		static char *x,*y;
      	};
      };
      char* Outer::x="Outer::x";
      char* Outer::Inner::x="Inner::x";
      char* Outer::Inner::y=x;
      int main(int argc, char *argv[]){	
      	cout<<Outer::Inner::y<<endl;	// Inner::x
      }
    • 一旦类的静态数据成员被定义过,即使不存在该类的对象,它的静态数据成员也是存在的。
    • If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression. In that case, the member can appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.
    • 类静态数据成员只能在程序中定义一次。
    • 匿名类或包含在匿名类中定义的类不能包含静态数据成员,因为无法初始化这些数据成员,但是可以包含静态函数成员。
    • 类的静态成员指定为外部链接(Static data members of a class in namespace scope have external linkage)
    • 局部类不能拥有静态数据成员。
      void func(){
      	struct Local{
      		static int i;	// error!
      	}x;
      }
    • 类的静态数据成员的初始化和销毁和非局部对象一样。
    • 静态数据成员不能为mutable。
  • 如果在定义一个静态对象时没有知道构造函数参数,这个类对象就必须有默认构造函数。
  • 名字空间范围内 显式声明为static的对象、引用、函数或函数模板被指定为内部链接。
  • 如果类为外部链接则其静态数据成员也为外部链接(A member function, static data member, class or enumeration of class scope has external linkage if the name of the class has external linkage).

初始化

程序的启动阶段包括名字空间中静态对象的初始化过程。

非局部对象

  1. 拥有非局部静态对象应该在所有初始化之前先进行零初始化。零初始化和常量表达式初始化统称为静态初始化,其余的初始化为动态初始化。 
  2. 拥有常量表达式的POD类型的非局部静态对象的初始化应该在所有动态初始化之前进行。
  3. 同一编译单元的名字空间中静态对象的动态初始化过程按照它们的定义出现的顺序依次进行。对于多个翻译单元的静态对象,不能保证严格的初始化顺序,也无法来指定这种顺序。
  4. An implementation is permitted to perform the initialization of an object of namespace scope with static storage duration as a static initialization even if such initialization is not required to be done statically, provided that
    — the dynamic version of the initialization does not change the value of any other object of namespace scope with static storage duration prior to its initialization, and
    — the static version of the initialization produces the same value in the initialized object as would be produced by the dynamic initialization if all objects not required to be initialized statically were initialized dynamically.
  5. 如果非局部对象的构造或者析构抛出了未捕获的异常则调用 terminate.

局部对象

  1. 局部静态对象应该在所有初始化之前先进行零初始化。
  2. 拥有常量表达式的POD类型的局部静态对象的初始化应该其所在块第一次进入之前初始化。
  3. An implementation is permitted to perform early initialization of other local objects with static storage duration under the same conditions that an implementation is permitted to statically initialize an object with static storage duration in namespace scope. Otherwise such an object is initialized the first time control passes through its declaration; such an object is considered initialized upon the completion of its initialization.
  4. 如果局部对象的初始化抛出异常则初始化未完成,下次进入其声明的时候将会再次尝试初始化。If control re-enters the declaration (recursively) while the object is being initialized, the behavior is undefined.

销毁

程序的终止过程包含了静态对象的销毁。

  • 只要局部静态对象被构建了,则其析构将会被运行。
    struct X{
    	~X(){cout<<"~X()"<<endl;}
    };
    struct Y{
    	~Y(){cout<<"~Y()"<<endl;}
    };
    
    void f(){static X x;}
    void g(){static Y y;}
    int main(int argc, char *argv[]){	
    	f();	// ~X()
    }
  • 如果在静态对象销毁时调用了exit 函数,程序的行为未定义。
  • 静态对象销毁的顺序与其动态初始化的顺序相反。如果静态对象为静态初始化,其销毁顺序跟其如果被动态初始化一样。
  • 对于一个数组或类对象,其所有子对象在其构造时任何初始化过的局部静态对象销毁之前销毁。(For an object of array or class type, all subobjects of that object are destroyed before any local object with static storage duration initialized during the construction of the subobjects is destroyed.)
  • 如果一个函数的局部静态对象的已经被销毁且此函数在另外一个静态对象的析构中被调用,程序的行为未定义。
  • 如果一个函数使用atexit函数注册了,则在此函数注册之前的所有静态对象在此函数执行完成之前不会被销毁,然而在此函数注册之后的所有静态对象销毁之前此函数不会被执行。如果 atexit 函数在一个对象的构造时被调用,则该对象所属的完全对象在此函数被调用之前被销毁。
  • 调用声明在<cstdlib>中的 abort() 函数将会终止程序且不执行所有自动对象和静态对象的销毁,且不执行穿给 atexit() 的函数。

PS:本文是根据C++ 2003标准整理而得,欢迎大家讨论