static的作用

  一:在C语言中,static的字面意思很容易把我们导入歧途,其实它的作用有三条。

1)先来介绍它的第一条也是最重要的一条:隐藏。

当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c

下面是a.c的内容

char a = 'A'; // global variable
void msg() 
{
    printf("Hello\n"); 
}

下面是main.c的内容

int main(void)
{    
    extern char a;    // extern variable must be declared before use
    printf("%c ", a);
    (void)msg();
    return 0;
}

程序的运行结果是:

A Hello

你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。

如果加了static,就会对其它源文件隐藏。例如在amsg的定义前加上staticmain.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。

2static的第二个作用是保持变量内容的持久。

存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。虽然这种用法不常见,但我还是举一个例子。

#include <stdio.h>

int fun(void){
    static int count = 10;    // 事实上此赋值语句从来没有执行过
    return count--;
}

int count = 1;

int main(void)
{    
    printf("global\t\tlocal static\n");
    for(; count <= 10; ++count)
        printf("%d\t\t%d\n", count, fun());    
    
    return 0;
}

程序的运行结果是:

global          local static

              10

              9

              8

              7

              6

              5

              4

              3

              2

10              1

(3)static的第三个作用是默认初始化为0

其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加’\ 0’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是’\0’。不妨做个小实验验证一下。

#include <stdio.h>

int a;

int main(void)
{
    int i;
    static char str[10];

    printf("integer: %d;  string: (begin)%s(end)", a, str);

    return 0;
}

程序的运行结果如下

integer: 0; string: (begin)(end)

最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0。

二、面向对象的static关键字(类中的static关键字)

1、静态数据成员

  在类内数据成员的声明前加上关键字static,该数据成员就是类内的静态数据成员。先举一个静态数据成员的例子。

//Example 5

  #include <iostream.h>

  class Myclass

  {

  public:

  Myclass(int a,int b,int c);

  void GetSum();

  private:

  int a,b,c;

  static int Sum;//声明静态数据成员

  };

  int Myclass::Sum=0;//定义并初始化静态数据成员

  Myclass::Myclass(int a,int b,int c)

  { this->a=a;

    this->b=b;

    this->c=c;

    Sum+=a+b+c;

     }

  void Myclass::GetSum()

  {

       cout<<"Sum="<<Sum<<endl;

   }

  void main()

  { Myclass M(1,2,3);

    M.GetSum();

    Myclass N(4,5,6);

    N.GetSum();

    M.GetSum();
}

可以看出,静态数据成员有以下特点:

  对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;

  静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。在Example 5中,语句int Myclass::Sum=0;是定义静态数据成员;

  静态数据成员和普通数据成员一样遵从public,protected,private访问规则;

  因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它;

  静态数据成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式为:

  <数据类型><类名>::<静态数据成员名>=<值>

  类的静态数据成员有两种访问形式:

  <类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>

  如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员 ;

  静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了;

  同全局变量相比,使用静态数据成员有两个优势:

  静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性;

  可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;

 2、静态成员函数

  与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部服务而不是为某一个类的具体对象服务。静态成员函数与静态数据成员一样,都是类的内部实现,属于类定义的一部分。普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。通常情况下,this 是缺省的。如函数fn()实际上是this->fn()。但是与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。下面举个静态成员函数的例子。

//Example 6

  #include <iostream.h>

  class Myclass

  {public:

  Myclass(int a,int b,int c);

  static void GetSum();/声明静态成员函数

  private:

  int a,b,c;

  static int Sum;//声明静态数据成员

  };

  int Myclass::Sum=0;//定义并初始化静态数据成员

  Myclass::Myclass(int a,int b,int c)

  { this.a=a;

    this.b=b;

    this.c=c;

    Sum+=a+b+c; //非静态成员函数可以访问静态数据成员

  }

  void Myclass::GetSum() //静态成员函数的实现

  {// cout<<a<<endl; //错误代码,a是非静态数据成员

  cout<<"Sum="<<Sum<<endl;

  }

  void main()

 { Myclass M(1,2,3);

  M.GetSum();

  Myclass N(4,5,6);

  N.GetSum();

  Myclass::GetSum();

 }
关于静态成员函数,可以总结为以下几点:

  出现在类体外的函数定义不能指定关键字static;

  静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;

  非静态成员函数可以任意地访问静态成员函数和静态数据成员;

  静态成员函数不能访问非静态成员函数和非静态数据成员;

  由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长;

  调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以直接使用如下格式:

  <类名>::<静态成员函数名>(<参数表>)

  调用类的静态成员函数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值