static

7 篇文章 1 订阅
3 篇文章 0 订阅

 

以为自己完全弄懂了static,今天在使用的时候还是出现了问题。

static有两种用法:面向过程程序设计中的static和面向对象程序设计中的static。

在面向对象设计中,静态成员变量在类中声明,在类外还要进行初始化,这点要注意。具体的解释见下面的文章:

摘自:http://baike.baidu.com/view/536145.htm

C++中

简介

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

面向过程的static

全局变量前,加上 关键字static,该变量就被定义成为一个 静态 全局变量。我们先举一个 静态 全局变量的例子,如下:
//Example 1
#include <iostream.h>
void fn();
static int n; //定义 静态全局变量
void main()
{ n=20;
cout<<n<<endl;
fn();
}
void fn()
{ n++;
cout<<n<<endl;
}
静态 全局变量有以下特点:
变量在全局数据区分配内存;
未经初始化的静态全局变量会被程序自动初始化为0(在函数体内声明的自动变量的值是随机的,除非它被显式初始化,而在函数体外被声明的自动变量也会被初始化为0);
静态 全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的;
静态变量都在全局数据区分配内存,包括后面将要提到的静态 局部变量。对于一个完整的程序,在内存中的分布情况如下图:
代码区 //low address
全局数据区
堆区
栈区 //high address
一般程序把新产生的 动态数据存放在堆区,函数内部的 自动变量存放在栈区。 自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静 态 局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。细心的读者可能会发现,Example 1中的代码中将
static int n; //定义静态 全局变量
改为
int n; //定义 全局变量
程序照样正常运行。
的确,定义 全局变量就可以实现变量在文件中的共享,但定义 静态全局变量还有以下好处:
静态 全局变量不能被其它文件所用;
其它文件中可以定义相同名字的 变量,不会发生冲突;
您可以将上述示例代码改为如下:
//Example 2//File1
#include <iostream.h>
void fn();
static int n; //定义静态全局变量
void main()
{ n=20;
cout<<n<<endl;
fn();
}
//File2
#include <iostream.h>
extern int n;
void fn()
{ n++;
cout<<n<<endl;
}
编译并运行Example 2,您就会发现上述代码可以分别通过编译,但运行时出现错误。试着将
static int n; //定义静态全局变量
改为
int n; //定义全局变量
再次编译运行程序,细心体会全局变量和 静态全局变量的区别。
注意: 全局变量和全局 静态变量的区别
1) 全局变量是不显式用static修饰的全局变量,但全局变量默认是动态的, 作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。
2)全局 静态变量是显式用static修饰的 全局变量作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。
局部变量前,加上 关键字static,该变量就被定义成为一个 静态局部变量。
我们先举一个静态 局部变量的例子,如下:
//Example 3
#include <iostream.h>
void fn();
void main()
{ fn();
fn();
fn();
}
void fn()
{ static int n=10;
cout<<n<<endl;
n++;
}
通常,在函数体内定义了一个 变量,每当程序运行到该语句时都会给该 局部变量分配栈内存。但随着程序退出 函数体,系统就会收回栈内存, 局部变量也相应失效。
但有时候我们需要在两次调用之间对 变量的值进行保存。通常的想法是定义一个 全局变量来实现。但这样一来, 变量已经不再属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。
静态 局部变量正好可以解决这个问题。静态 局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。
静态 局部变量有以下特点:
该变量在全局数据区分配内存;
静态 局部变量在程序执行到该对象的声明处时被首次初始化,即以后的 函数调用不再进行初始化;
静态 局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
它始终驻留在全局数据区,直到程序运行结束。但其 作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
在函数的返回类型前加上static 关键字,函数即被定义为 静态函数静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。
静态函数的例子:
//Example 4
#include <iostream.h>
static void fn();//声明 静态函数
void main()
{
fn();
}
void fn()//定义 静态函数
{ int n=10;
cout<<n<<endl;
}
定义 静态函数的好处:
静态函数不能被其它文件所用;
其它文件中可以定义相同名字的函数,不会发生冲突;

面向对象的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 指针的额外开销,因此 静态成员函数与类的 全局函数相比速度上会有少许的增长;
调用 静态成员函数,可以用成员访问操作符(.)和(->;)为一个类的对象或指向类对象的指针调用静态成员函数,也可以直接使用如下格式:
<;类名>::<;静态成员函数名>;(<;参数表>;)
调用类的 静态成员函数。

作用

static 静态变量声明符。在声明它的程序块, 子程序块或函数内部有效,值保持,在整个程序期间分配存储器空间, 编译器默认值0。
是C++中很常用的 修饰符,它被用来控制变量的存储方式和可见性。

为什么要引入static

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

什么时候用static

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

内部机制

静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。
这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有 类的成员函数定义;三是 应用程序的main()函数前的全局数据声明和定义处。
静态 数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的 内存分配,所以在类声明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的 源文件中,对其重复定义。
static被引入以告知 编译器,将 变量存储在程序的 静态存储区而非栈上空间,静态
数据成员按定义出现的先后顺序依次初始化,注意 静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。

优势

可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。

应用格式

引用静态数据成员时,采用如下格式:
<;类名>::<;静态成员名>
如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员。

注意事项

⑴类的 静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。
⑵不能将 静态成员函数定义为虚函数。
⑶由于 静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember 函数指针”。
⑷由于 静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X Window系统结合,同时也成功的应用于 线程函数身上。
⑸static并没有增加程序的时空开销,相反她还缩短了子类对父类 静态成员的访问时间,节省了子类的内存空间。
⑹静态数据成员在<;定义或说明>;时前面加 关键字static。
⑺静态数据成员是静态存储的,所以必须对它进行初始化。
静态成员初始化与一般数据成员初始化不同:
初始化在类体外进行,而前面不加static,以免与一般 静态变量或对象相混淆;
初始化时不加该成员的访问权限控制符private,public等;
初始化时使用 作用域 运算符来标明它所属类;
所以我们得出静态数据成员初始化的格式:
<;数据类型><;类名>::<;静态数据成员名>=<;值>
⑼为了防止父类的影响,可以在子类定义一个与父类相同的 静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说 静态成员为父类和子类共享,但我们有重复定义了静态成员,这会不会引起错误呢?不会,我们的 编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志。在各通信公司的笔试面试中经常出现的考题就是static的作用及功能。

编辑本段C中

分类

static 函数内部函数和外部函数
当一个源程序由多个 源文件组成时,C语言根据函数能否被其它源文件中的 函数调用,将函数分为内部函数和外部函数。

内部函数

(又称 静态函数
如果在一个 源文件中定义的函数,只能被本文件中的 函数调用,而不能被同一程序其它文件中的函数调用,这种函数称为内部函数。
定义一个内部函数,只需在函数类型前再加一个“static” 关键字即可,如下所示:
static 函数类型 函数名(函数参数表)
{……}
关键字“static”,译成中文就是“ 静态的”,所以内部函数又称 静态函数。但此处“static”的含义不是指存储方式,而是指对函数的 作用域仅局限于本文件。
使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。

外部函数

外部函数的定义:在定义函数时,如果没有加 关键字“static”,或冠以关键字“extern”,表示此函数是外部函数:
[extern] 函数类型 函数名(函数参数表)
{……}
调用外部函数时,需要对其进行说明:
[extern] 函数类型 函数名(参数类型表)[,函数名2(参数类型表2)……];
[案例]外部函数应用。
⑴文件mainf.c
main()
{ extern void input(…),process(…),output(…);
input(…); process(…); output(…);
}
⑵文件subf1.c
……
extern void input(……) /*定义外部函数*/
{……}
⑶文件subf2.c
……
extern void process(……) /*定义外部 函数*/
{……}
⑷文件subf3.c
……
extern void output(……) /*定义外部函数*/
{……}

vb中

语句

在过程级别中使用,用于声明 变量并分配 存储空间。在整个代码运行期间都能保留使用 Static 语句声明的 变量的值。
static语句声明的 变量,与dim语句声明的变量的主要区别是:前者只能在sub或function过程中使用,在退出sub或function过程后变量的值保留;后者使用在sub或function过程中时,退出sub或function过程后变量的值不保留。

语法

Static varname[([subscripts])] [As [New] type] [,varname[([subscripts])] [As [New] type]] . . .
Static 语句的语法包含下面部分:

描述

varname 必需的。 变量的名称;遵循标准变量 命名约定
subscripts 可选的。 数组 变量的维数;最多可以定义 60 维的 多维数组。subscripts 参数使用下面的语法:
[lower To] upper [,[lower To] upper] . . .
如果不显式指定 lower,则 数组的下界由 Option Base 语句控制。如果没有 Option Base 语句则下界为 0。
New 可选的。用它可以隐式地创建对象的 关键字。如果使用 New 声明对象 变量,则在第一次引用该 变量时将新建该对象的实例,因此不必使用 Set 语句来对该对象引用赋值。New   关键字不能用来声明任何内部 数据类型的变量,也不能用来声明从属对象的实例。
type 可选的。 变量数据类型;可以是 Byte、Boolean、Integer、Long、Currency、Single、Double、Decimal(目前尚不支持)、Date、String(对变长的字符串)、String * length(对定长的字符串)、Object、Variant、用户定义类型或对象类型。所声明的每个 变量都要有一个单独的 As type 子句。

说明

模块的代码开始运行后,使用 Static 语句声明的 变量会一直保持其值,直至该模块复位或重新启动。可以在非 静态的过程中使用 Static 语句显式声明只在该过程内可见,但具有与包含该过程定义的模块相同 生命期变量
可以在过程中使用 Static 语句来声明在过程调用之间仍能保持其值的 变量数据类型。例如,下面的语句声明了一个定长的 整型 数组
Static EmployeeNumber(200) As Integer
下面的语句为 worksheet 的新实例声明了一个 变量
Static X As New Worksheet
如果在定义对象 变量时没有使用 New   关键字,则在使用该变量之前,必须使用 Set 语句将一个已有的对象赋给这个引用对象的变量。在被赋值之前,所声明的这个对象 变量有一个特定值 Nothing,这个值表示该变量没有指向任何对象的实例。若在声明中使用了 New 关键字,则在第一次引用对象时将新建一个该对象的实例。
如果不指定 数据类型或对象类型,且在模块中没有使用   Deftype  语句,则按缺省情况,定义该 变量为 Variant 类型。

注意

Static 语句与 Static   关键字很相似,但是针对不同的效果来使用的。如果使用 Static   关键字(如 Static Sub CountSales ())来声明一个过程,则该过程中的所有局部 变量的存储空间都只分配一次,且这些变量的值在整个程序运行期间都存在。对非静态过程而言,该过程每次被调用时都要为其变量分配 存储空间,当该过程结束时都要释放其变量的存储空间。Static 语句则用来在非静态的过程中声明特定的 变量,以使其在程序运行期间能保持其值。
在初始化 变量时,数值变量被初始化为 0,变长的字符串被初始化为一个零长度的字符串 (""),而定长的字符串则用 0 填充。Variant   变量被初始化为 Empty。用户自定义类型的 变量的每个元素作为各自独立的变量进行初始化。
注意 如果在过程中使用 Static 语句,应和其它的声明语句(如 Dim)一样将其放在过程的开始。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值