C/C++中static关键字的用法

(一)变量的分类

变量可以分为全局变量、静态全局变量、静态局部变量和局部变量这四种。这几种变量的特点如下:

(1) 按存储区域分,全局变量、静态全局变量和静态局部变量都存放在内存的静态存储区域,局部变量存放在内存的栈区。
(2) 按作用域
1.全局变量在整个工程文件内都有效;
2.静态全局变量只在定义它的文件内有效;
3.静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;
4.局部变量在定义它的函数内有效,但是函数返回后失效。

(3)三者之间的区别
1.当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 
2.静态全局变量限制了其作用域,只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于这种限制性,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
3.把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期,把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
4.static 函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
5.static全局变量只初始化一次,防止在其他文件单元中被引用;
6.static局部变量只被初始化一次,下一次依据上一次结果值;
7.static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝.
8.全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量的值不可知。

(二)C中的static关键字

1.静态全局变量
  在全局变量之前加上关键字static,该变量就变为了静态全局变量;
  特点:该变量在全局数据区分配内存,存储于内存中的静态存储区域;
        如果不显示初始化,那么变量会被隐式初始化为0;
        静态全局变量只在本源文件可见,从定义之处开始到本文件结束;不能被同一工程下的其他文件通过extern调用
 代码

//test.c

 int main
 {
    static n = 10;
    fun(n);
 }
 //main.c

 extern int n ;
 int main()
 {
    cout<<n<<endl;
 }

 main.c找不到int n 的定义点。

2.静态局部变量
   在局部变量前加上static关键字,该变量就变为了静态局部变量;
   特点
   该变量在全局数据区分配内存,存储于内存中的静态存储区域;
   如果不显示初始化,那么变量会被隐式初始化为0;
   静态全局变量始终驻留在全局数据区,直到程序运行结束;但它的作用域为局部作用域;
   当定义此变量的函数或语句块结束时,其作用域也随之消失
代码

#include <stdio.h>
#include <stdlib.h>


void fun()
{
    static int var1 = 0; //静态局部变量
    int var2 = 0;        //自动变量 
    var1 ++;//1
    var2 ++;//1

    printf("var1 = %d ", var1);
    printf("var2 = %d\n", var2);
}

int main() 
{
   int i = 0 ;
   for( ; i < 4 ;++i)
   {
	  printf("i = %d\n",i);
	 fun();
   }
    return 0;
}

执行结果如下图:

3.静态函数
  在函数的返回类型之前加上static关键字,函数就被定义为静态函数;
  特点
  静态函数只能本源文件可见;
  在文件作用域下声明的inline函数被默认为static类型;
 (二)C++中的static关键字
 1.静态数据成员
 (1)类中的数据成员的声明前加上static关键字,该数据就成了该类的静态数据成员并且只能被定义一次;
 (2)静态数据成员被类的所有对象所共享,包括类的派生类的对象;即派生类对象与基类对象共享基类的静态数据成员。
 (3)类的各个对象都拥有类的每一个普通数据成员的副本,但是静态数据成员只有一个实例存在,与定义了多少类对象无关;
 (4)静态数据成员也遵守public/protected/private访问规则;
 (5)静态数据成员的作用之一是统计有多少个对象实际存在;
 (6)静态数据成员属于类中的全局变量
     不能在类中初始化,不能在类的构造函数中初始化成员,否则每创建类的一个对象静态数据成员都要被重新初始化一次;
     不能在类体内进行赋值,由于它被所有该类的对象共享;当在一个对象里对静态数据成员进行赋值时,其他对象里的内容也会发生变化;
     为了避免发生赋值混乱,不可再类体内进行初始化;
(7)静态数据成员的值对所有对象是一样的,可以被初始化,但只能在类外初始化;
     初始化形式: 数据类型名 类作用域::静态数据成员 = 初值

代码如下:

#include <iostream>
using namespace std;

class A
{
public:
	A(int n = 0):c(n)
	{
		cout<<"A()"<<endl;
		cout<<"A::a = "<<a<<endl;
	}
	~A()
	{
		cout<<"~A()"<<endl;
	}
static int a ;
private:

	int c ;
};
int A::a = 1;//静态数据成员初始化
class B:public A
{
public:
	B(int n = 0):b(n)
	{
		cout<<"B::b = "<<b<<endl;
	}
	~B()
	{
		cout<<"~B()"<<endl;
	}
private:
	int b ;
};


int main()
{
	A x(2);
	B y(3);
	y.a = 10;
	cout<<x.a<<endl;//10 由于b可以访问基类A的静态数据成员,属于全局变量

	return 0;
}

2.静态成员函数
特点:
(1)静态成员函数不属于任何一个对象,与任何对象无关所以没有this指针;
     当调用一个对象的成员函数(非静态)时,系统会把该对象的地址赋给成员函数的this指针;而静态成员函数无this指针,
     没有指向某个对象,就无法对一个对象中的非静态成员进行默认访问(在引用数据成员时不指定对象名) 
     静态成员函数的地址可以被普通指针存储,而普通成员函数的地址需要用类成员函数指针存储;
     代码示例:
     class base
     { 
         static int func1(); 
         int func2(); 
     }; 

     int (*pf1)()=&base::func1;//普通的函数指针 
     int (base::*pf2)()=&base::func2;//成员函数指针
(2)出现在类体外的函数定义不能指定关键字static;
(3)静态成员之间可以互相访问,包括静态成员函数访问静态数据成员、访问静态成员函数;
(4)非静态成员函数可以任意地访问静态成员函数和静态数据成员;(由于被类的所有对象所共享)
     调用格式:类对象名.静态成员函数名() 
              类类型名.静态成员函数()
(5)静态成员函数不能访问非静态成员函数和非静态数据成员(无this指针指向要访问的对象)
(6)调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数;
(7)静态成员函数在类外初始化,初始化格式:
    类类型名 类作用域::初值对象
    HeapManager  Shared_Ptr<T> :: _HeapManager;
(8)静态成员函数不可以被同时声明为virtual、const、volatile函数
    1.静态成员函数不能成为虚函数
      (1)静态函数与类有联系。但与类的对象没有联系,并且不存在this指针;
  (2)虚函数依靠虚函数指针vfptr和虚函数表vtable来执行;vptr是一个指针,是对象的组成部分,在类的构造函数中创建生成,并且只能用this指针来访问它,
           vfptr指向保存虚函数地址的vtable.虚函数的调用关系:this -> vfptr -> vftable ->virtual function,其解释为this指针指向对象,然后在对象找到这个对象所拥有的vfptr,从而找到部分vfptr所指向的虚函数表,从表里找到虚函数的入口地址。
          对于静态成员函数,它没有this指针,所以无法访问vfptr. 这就是为何static函数不能为virtual.
    2.静态成员函数不能成为const函数
       (1)静态成员函数没有this指针;
      (2)const用于"后置修饰函数时"只用于限定成员函数,意味着将被修饰的成员函数的隐式参数——this指针
       由原来的Class* const变为const Class* const类型,使得在该成员函数内不能修改成员属性,除非该属性被mutable修饰;
       static类函数并没有隐式的this指针,因为其本质上还是属于C函数——满足_cdecl调用协定。

猜你喜欢

转载自blog.csdn.net/weixin_41966991/article/details/81081957