四种不同对象的生存方式(栈、堆、全局、局部静态)
[结果分析,引申出四种对象]:
|
生存方式 |
执行时机 |
消亡时机 |
全局(静态)对象 |
全局静态存储区global |
比程序进入点更早,构造函数先被执行; |
程序结束前,其析构函数被执行。 |
局部静态对象 |
局部静态存储区local static |
在对象诞生时,其构造函数被执行。(注意,此处只会有一个实例产生,而且固定在内存上(非stack也非heap),它的构造函数在控制权第一次移转到其声明处时被调用。 |
程序将结束时(此对象因而将遭致毁灭)其析构函数才被执行,但比全局对象的析构函数更早一步执行。 |
局部对象 |
栈区 stack |
在对象诞生时,其构造函数被执行。(同局部静态对象) |
程序流程将离开该对象的存活范围时(以至于该对象将销毁)时,其析构函数被执行。 |
new方式产生的局部对象 |
堆区 heap |
当对象诞生时,其构造函数被执行。(同局部静态对象、局部对象) |
在对象被delete时执行析构函数。(注意,不加delete该heap区空间不会自动释放的,如果程序长期运行,会“吃掉”很多内存,造成结果不可预知。) |
[静态对象的深入探究(全局、局部)]
|
从产生抑制持续到程序结束的那些对象,在这个过程中不会动态的消亡,所以叫静态对象。 |
|
|
全局静态对象 |
局部静态对象 |
1.初始化时机 |
1)在main函数的代码前进行初始化; 2)类中静态、全局对象的初始化时机与该类的对象并无关系(强调:出现在类定义中的静态变量语句只是声明,对于要使用的类的静态成员变量,必须还要在类外进行定义,否则使用时会发生链接错误。声明并不会导致空间的分配,只有定义才会使其被生成。也就是如果你对类的静态成员进行了定义,那么它就肯定会被分配空间并初始化。就像全局变量一样); |
初始化发生在函数被调用期间,首次碰到该定义时。 |
2.举例(区分全局、局部静态对象) |
1)定义于namespace的对象; 2)在class, file里的static对象; 3)类中的静态变量和全局变量; |
定义在函数里的为局部静态对象 |
3.如何实现的? |
对于non-local静态变量的初始化,编译器实际上是这样实现的。对每个编译文件里需要初始化的静态变量,生成一系列的sti_开头的函数,在里面完成初始化调用语句,然后把这些sti_函数抽取出来,形成一个初始化函数表,然后在__main()函数里调用,然后把这个函数放到main里的开头。 |
而对于local静态变量,为了实现第一次碰到时初始化,附加了一个全局变量,标志该对象是否被初始化,同时在析构时检查这个变量。这样就保证了第一次碰到时初始化,同时只有在执行了构造函数的时候,才会在程序退出时执行对应的析构函数。 |
全局变量与全局静态变量的区别:
(a)若程序由一个源文件构成时,全局变量与全局静态变量没有区别。
(b)若程序由多个源文件构成时,全局变量与全局静态变量不同:全局静态变量使得该变量成为定义该变量的源文件所独享,即:全局静态变量对组成该程序的其它源文件是无效的。
静态全局变量的作用:
(a)不必担心其它源文件使用相同变量名,彼此相互独立。
(b)在某源文件中定义的静态全局变量不能被其他源文件使用或修改。
例如:一个程序由两个源文件组成,其中在一个源文件中定义了“int n;”,在另一个源文件中定义了“static int n;”则程序给它们分别分配了不同的空间,两个值互不干扰。
例如:下面在file1.cpp中声明全局变量n,在file2.cpp中定义全局静态变量n。文件file1.cpp和file2.cpp单独编译都能通过,但连接时,file1.cpp中的变量n找不到定义,产生连接错误。
// file1.cpp # include void fn() extern int n; void main() { n=20; cout<<n<<endl; p="" <=""> fn(); } // file2.cpp # include static int n; // 默认初始化为0,注意此处定义的n 只能在file2.cpp中使用。 void fn() { n++; cout<<n<<endl; p="" <=""> }
静态函数:使某个函数只在一个源文件中有效,不能被其他源文件所用。
定义:在函数前面加上static。
说明:函数的声明和定义默认情况下在整个程序中是extern的。
静态函数的效果:
(1)它允其他源文件建立并使用同名的函数,而不相互冲突。
(2) 声明为静态的函数不能被其他源文件所调用,因为它的名字不能得到。
拙见:
静态变量和函数一般都局限于一个编译单元也就是.cpp文件中。
静态变量和私有变量的最主要的区别就在于:他们分配内存空间的方式不一样。静态变量的内存是在程序开始执时变量就占用了内存,直到程序结束时变量才释放内存.私有变量(或者说是局部变量,不知道你是不是指这个:)),是在程序运行到该步的时候分配内存。所以,当离开了该私有变量的作用域的时候,私有变量的内存空间会被释放。所以:静态变量只的值只会初始化一次,后面每次访问,都是上次处理过的值,(即使是在一个函数内部)。私有变量每次都初始化。看下面的实践:
class Program { static void Main(string[] args) {//输出未经定义的静态变量,结果为0;也说明了,在C#中未赋初值的变量系统自动赋为0 Console.WriteLine(sort.i); //静态变量的访问方法(类名.静态变量名),而且还可以在外部操作静态变量呢,可见静态变量并不神秘; sort.i = 5; //输出5 Console.WriteLine(sort.i); //还可以通过构造函数对静态变量初值呢,呵 sort sortTest = new sort(); //输出构造函数中的赋值 3; Console.WriteLine(sort.i); } } class sort { public static int i; public sort() { i = 3; } }
总结:在类内部访问静态变量时,直接用静态变量名即可,不用以(类名.静态变量名),这样的方式访问,除了有静态变量之外,还有静态类实例,还有静态方法.但用法都是大同小异;(没有静态类哦,呵呵越论越傻了),如:
public static void myFun(){} //静态方法
private static Random MyRandom=new Random(); //静态类实例
之所以有时声明为私有静态变量,是为了让它只初始化一次.这样节省了内存空间但又想让它在外部是不可访问的,这样利用私有这个访问限定符就搞定了. 私有静态:安全又节省空间.
例:如果想在每次实例化类的时间生成一组随机数,但产生随机数是要用到一个类的,即Random,这个类不是静态类,它要产生实例,用产生的实例来生成随机数,但如果在每次类实例化时都产生一个Random实例,那内存空间简直是极大的浪费,所以可以用:
private static Random MyRandom=new Random();
这样每次类实例化时,都会用同一个Random实例MyRandom来产生随机数