static关键字用法

1 static关键字

2 为什么要引入static

我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义为全局的变量,但定义一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅只受此函数控制)。static 关键字则可以很好的解决这个问题。

另外,在 C++ 中,需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。

3 C/C++中static的作用

由于C++是一种源自于C的语言,所以我们需要先分析staticC语言中的应用。在C语言中, 使用static关键字的作用是:

  • 用于函数内部修饰变量,即函数内的静态变量。这种变量的生存期长于该函数,使得函数具有一定的“状态”。使用静态变量的函数一般是不可重入的,也不是线程安全的,比如strtok(3)。

  • 用在文件级别(函数体之外),修饰变量或函数,表示该变量或函数只在本文件可见,其他文件看不到也访问不到该变量或函数。

C++中引出的C语言没有的类,所以我们就需要分析在C++的类里面使用static关键字的作用与方式,其作用是:

  • 用于修饰类的数据成员,即所谓“静态成员”。这种数据成员的生存期大于class的对象。静态数据成员是每个class有一份,普通数据成员是每个对象有一份。

  • 用于修饰class的成员函数,即所谓“静态成员函数”。这种成员函数只能访问静态成员和其他静态程员函数,不能访问非静态成员和非静态成员函数。

4 C++中static的用法

C++中的static有两种用法:面向过程程序设计的static和面向对象程序设计中的static,前者应用于普通变量和函数,不涉及类;后者主要说明static在类中的作用。

对于静态变量和类中的静态数据成员来说,在编译的时候就确定初始值,程序启动后,执行main函数前初始化,后续就不再初始化,保证只初始化一次;

对于静态函数、类的函数和类的实例来说,在运行的时候确定初始值,编译器会在static变量初始化前插入一个if语句块,判断static标志位是否为0,该标志位的初始值为0,当第一次执行if语句时会为true,进入if语句块后,首先设置static标志位为1,然后初始化static变量,由于static标志位为1,就再也不会进入if语句块,保证只初始化一次。

4.1 面向过程设计中的static

4.1.1 静态全局变量

在全局变量前,加入关键字static,该变量就被定义成为一个静态全局变量。如下:

//静态全局变量和全局变量都是在内存中只有一份拷贝,可以被很多函数修改。
#include<iostream>
using namespace std;
static int i = 5;  //静态全局变量
int j = 3;//全局变量,默认为extern
int main()
{
    /*code*/
}

静态全局变量i和全局变量j都存放于静态/全局存储区,它们的生存周期都是程序的整个运行期,但是j的作用域为全局作用域,可以通过extern在其他文件中使用,而i的作用域为文件作用域,只能在当前cpp文件中使用。

如果在另一个文件cpp文件中写下如下代码:

extern int j; //OK,因为j为全局变量,在整个工程下都可见
extern int i; // error: i为静态全局变量,在该文件中不可见
int a = j; // OK
int b = i; // error

可见,静态全局变量有以下特点:

①在静态/全局存储区分配内存;

②未初始化的静态全局变量会被程序自动初始化为0;

③作用域:静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的,因此在其他文件中可以定义相同名字的变量,不会发生冲突。

全局变量和静态全局变量的区别在于:

①全局变量是不显式用static修饰的全局变量,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern外部声明后就可以使用全局变量

②静态全局变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用

4.1.2 静态局部变量

在局部变量前,加入关键字static,该变量就被定义成为一个静态局部变量。如下:

#include<iostream>
using namespace std;
void fun()
{
    int i = 1; // 局部变量,具有动态生存期,每次进入函数时都初始化
    static int j = 2; //静态局部变量,具有全局寿命,局部可见,在第一次进入函数时被初始化
}
int main()
{
    static int j; //静态局部变量,具有全局寿命,局部可见
}

对于函数体内的局部变量来说,当程序运行到局部变量的定义时就会给该变量分配栈内存,但随着程序退出该函数体,系统就会收回栈内存,局部变量也相应的失效。如果想在多次调用该函数之间对变量的值进行保存,通常的想法是定义一个全局变量来实现,但是全局变量的作用域又会扩大我们想要的作用域,给程序的维护带来了不便。

静态局部变量解决了这个问题,它保存在全局/静态存储区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。如上,在fun()函数中,在第一次被调用该函数时会对j进行一次初始化,以后再调用该函数时,就不会执行这行代码static int j = 2;

静态局部变量有以下特点:

①在全局/静态存储区分配内存

②静态局部变量在程序执行到该变量声明处时会被首次初始化,在以后的函数调用时不再进行初始化

③静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0

④作用域和生命周期:在程序运行结束时才消亡,但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束

局部变量和静态局部变量的区别:
①生命周期上:局部变量是函数结束时被清理,静态局部变量是程序结束时被清理
②作用域上:都是局部作用域
③存储上:局部变量是在栈区上,静态局部变量是在全局/静态存储区上
④局部变量:编译器一般不对普通局部变量进行初始化,即它的值在初始时是不确定的,除非对其显式赋值;静态局部变量:只初始化一次。

4.1.3 静态函数

在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。如下:

#include<iostream>
using namespace std;
static void fun()
{
    /*code*/
}
int main()
{
    /*code*/
}

定义静态函数的好处:

①静态函数不能被其它文件所用 (限定作用域)

②其它文件中可以定义相同名字的函数,不会发生冲突

4.2 面向对象程序设计中的static(类中的static关键字)

4.2.1 静态数据成员

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

#include<iostream>
using namespace std;
 
class Point
{
public:
	Point(int xx = 0, int yy = 0) { X = xx; Y = yy; countP ++; }
	Point(Point &p); 
	~Point() { countP --; }
	int GetX() { return X; }
	int GetY() { return Y; }
	void GetC() { cout << "Object id = " << countP << endl; } //输出静态数据成员
private:
	int X, Y;
	static int countP; //静态数据成员声明,用于记录点的个数
};
 
Point :: Point(Point &p)
{
	X = p.X;
	Y = p.Y;
	countP ++; //在构造函数中对countP累加,所有对象共同维护同一个countP
}
 
//注意:静态成员变量,必须在类外初始化,且要去掉static关键字 
int Point :: countP = 0; //静态数据成员定义和初始化,使用类名限定
 
int main()
{
    Point A(4, 5); // 定义对象A,其构造函数会使countP增1
    A.GetC(); //输出对象个数
    Point B(A); //定义对象B,其构造函数会使countP增1
    B.GetC();
 
    return 0;
}

静态数据成员特点:
①静态数据成员存储在全局/静态存储区,因为静态数据成员独立该类的任意对象存在,它是与类关联的对象,不与类对象关联, 且在定义时分配存储空间,所以不能在类声明中定义(在类内声明,类外定义)

  • 只能在类外初始化
  • 不能通过初始化列表初始化
  • 不能在类内进行初始化
  • 不能在构造函数中初始化

②只有一个拷贝

③不属于某个对象,在没有对象时可以直接用类名访问,在为对象分配空间时不包括静态成员所占空间

④所有对象共享这个资源,可以通过类对象访问

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

同全局变量相比,使用静态数据成员有两个优势:
①静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其他全局名字冲突的可能性;

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

4.2.2 静态成员函数

在类定义中,前面有static说明的成员函数称为静态成员函数。

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

#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();
}

静态成员函数特点:

① 静态成员函数属于整个类,而不属于某一个对象,只有一份拷贝,在对象没有创建前,即可通过类名调用

②静态成员函数没有this指针,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用静态数据成员和静态成员函数

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

④静态成员函数也有访问权限

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值