您的位置:首页 > 其它

面向过程和面向对象的static关键字区别

2016-12-26 16:16 393 查看
参考文章:

static-搜狗百科

static的作用

一、面向过程语言的static(例如c)

在面向过程语言中,static主要有三个作用:

(1)存储的内容持久化;函数在栈上分配的空间在此函数执行结束时会释放掉,而静态的变量或函数分配到全局数据区的内存中,直到程序结束才被释放。

(2)控制文件间指定数据的可见性;静态的变量或函数仅能在声明的文件中访问,对其他文件不可见。

(3)默认初始化为0。全局变量也具备这一属性,减少了置0操作。

1、静态全局变量

        对于一个完整的程序,在内存中的分布情况如下图:  

        ——————————

        | 代码区 //low address |

        | 全局数据区               |

        | 堆区                          |

        | 栈区 //high address |

        ——————————

        一般程序把新产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(包括函数内部的静态局部变量)存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。

静态全局变量的特点:

(1)静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的,类似于c#的internal。

(2)其它文件中可以定义相同名字的变量,不会发生冲突。

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

(1)全局变量默认是动态的,作用域是整个工程,在声明之外的文件中,通过extern 全局变量名的声明,就可以使用全局变量;而静态全局变量即使通过extern也无法访问。

2、静态局部变量

        执行到在函数体中的普通变量时,会给它分配栈内存。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。

静态局部变量特点:

(1)静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化。

(2)它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。

3、静态函数

        与静态全局变量相同。

二、面向对象语言的static(例如c#)

1、静态数据成员

        对于非静态数据成员,该类每个对象都有自己的副本,而静态数据成员被当作是类的成员,而不专属某个对象。也就是说,对该类的所有对象来说,静态数据成员只分配一次内存,这块内存对所有对象共享的,所有对象获取它的值都是同一个。通常用于每个对象都相同的某个属性,且是需要保持一致的属性,比如存款年利率。

静态数据成员特点:

(1)某一个对象对其更新,该类的所有对象都将得到相同的新值。

(2)静态数据成员定义时要分配空间,所以不能在类声明中定义(应该是针对c++的声明定义方式)。

(3)静态数据成员和普通数据成员一样遵从public,protected,private访问规则。例如private static int i;只能在类中访问,虽然是静态数据。

静态数据成员相对于全局变量的优势:

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

(2)可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能。(应该又是针对c++的定义全局变量)

2、静态成员函数

        静态成员函数与静态数据成员一样,都是类的内部 实现,属于类定义的一部分,为该类而不是某一个对象服务。

静态成员函数特点:

(1)出现在类体外的函数定义不能指定关键字static(又是c++)。

(2)不能使用this指针指向该类的对象本身。因为它不属于任何一个对象。

(3)静态成员函数不能访问非静态成员函数和非静态数据成员,只能访问类的静态成员函数和静态数据成员。

三、什么情况下使用static

1、需要将函数中此变量的值保存至下一次调用。

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

四、内部机制

        静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。

        因此,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;(按文章的知识来说,声明是不分配内存的,这里是不是出错?)二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以在类声明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。

         static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。

五、注意事项

1、类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。 

2、不能将静态成员函数定义为虚函数。 

3、由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember函数指针”。 

4、由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X Window系统结合,同时也成功的应用于线程函数身上。 

5、static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问时间,节省了子类的内存空间。

6、静态数据成员是静态存储的,所以必须对它进行初始化。

7、静态成员初始化与一般数据成员初始化不同:(又是c++) 

        初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;

        初始化时不加该成员的访问权限控制符private,public等;

        初始化时使用作用域运算符来标明它所属类;

        所以我们得出静态数据成员初始化的格式:  <;数据类型><;类名>::<;静态数据成员名>=<;值>

8、为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但我们有重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志。(肯特指定c++,其他语言有待考察)

9、static修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。(特指c#)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  全局变量 静态