C# 学习之5C++ /C/C# static

static 是由最初的限制和持久过渡到后的共享。
C语言时代的static 
       在C语言中,只存在函数和变量。
             A、函数和全局变量,一旦加上static关键字,就限制了其作用域为本文件有效。
             B、局部变量,一旦加上static关键字,其生命其为整个应用程序。
C++语言时代的static
      在C++语言中,除了向C语言兼容外,提供了类的支持,同样static在C语言的特性在C++同样有效。
             C、类的static关键字,在C++的类中,static只能用于修饰数据成员和函数成员(不能修饰构造函数和析构函数),用于该类所具有的信息而不属具体某个对象.
             D、<注意>如果在类的成员函数中定义了static局部变量,其值会在所有对象中共享!这一点不好!
C#语言时代的static
       在C#语言中,基于类static访问特性和C++相似。同时,也有了一些改变。
             E、在C#中,禁止了类成员函数中的局部变量用static修饰。
             F、类的静态成员字段的初始化更加简单,可以直接在类定义中赋值。初始值设定是按照文本顺序执行,如果类中有静态字段而无静态构造函数,系统会自动生成,并先于静态构造函数执行。
             G、类提供了static修饰构造函数。类的静态构造函数在给定应用程序域中至多执行一次:只有创建类的实例或者引用类的任何静态成员才激发静态构造函数 

可见static从C-->C++是由简入繁。
C#作为一种全新的语言,他消除了C++语言的一些复杂性,全面支持面向对象。所以没有C语言的全局函数和全局变量。类成员函数内也不支持静态成员变量了。
下面用下事例代码,全面阐述以上内容

 

/
// C语言事例A
// 用来表示不能被其它文件访问的全局变量和函数。, 但为了限制全局变量/函数的作用域, 函数或变量前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。注意此时, 对于外部(全局)变量, 不论是否有static限制, 它的存储区域都是在静态存储区, 生存期都是全局的. 此时的static只是起作用域限制作用, 限定作用域在本模块(文件)内部.
// 使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。
// c2.c
int  g_i = 0 ;
static   int  g_is = 0 ;

int  f1()
{
    
return 1;
}

static   int  f2()        
{
    
return 2;
}

// c1.c
extern   int  g_i;         // 引用文件c3.h的全局变量
extern   int  g_is;     // 声明有一个外部全局变量g_is
extern   int  f1();
extern   int  f2();    
#include 
< stdio.h >
void  main()
{
    printf(
"%d\n",g_i);
    
//printf("%d\n",g_is);    //报错
    printf("%d\n",f1());
    
//printf("%d\n",f2());    //报错,不能访问的函数.
}

// C语言事例B
// 局部变通变量与局部静态变量的区别
// 1. 存储空间分配不同
// auto类型分配在栈上, 属于动态存储类别, 占动态存储区空间, 函数调用结束后自动释放, 而static分配在静态存储区, 在程序整个运行期间都不释放. 两者之间的作用域相同, 但生存期不同.
// 2. static局部变量在所处模块在初次运行时进行初始化工作, 且只操作一次
// 3. 对于局部静态变量, 如果不赋初值, 编译期会自动赋初值0或空字符, 而auto类型的初值是不确定的. 
// c3.c
#include  < stdio.h >
void  Print()
{
    
static int a=0;
    
int b=0;
    a
++;        
    b
++;
    printf(
"a = %d,b = %d \n",a,b);
}

void  main()
{
    
for (int i=0; i<3; i++)
        Print();
}

// 会发现a的会记住上一次的值。具有不可重入性。

// C++语言事例C
// 很多时候需要将类看作一个整体,当这个整体的一些相关信息就可以用static来维护和管理
// c4.c
#include  < iostream.h >

class  Test
{
private:
    
int a;
    
static char creator[16];
public:
    
static char* const GetCreator() 
    
{
        
//a = 1;            //静态成员函数不能访问类的非静态字段
        return creator;
    }

}
;
char  Test::creator[ 16 ] = " 这是Fung建的类! " ;

void  main()
{
      cout
<<Test::GetCreator()<<endl;
}

// C++语言事例D
// 尽量不要在类的成员函数中定义static变量,这可能导致程序混乱.
// c5.c
#include  < iostream.h >

class  Test
{
public:
    
void fun()
    
{
        
static int a=0;
        a 
+= 3;
        cout
<<a<<endl;
    }

}
;

void  main()
{
    Test
* t = new Test();
    t
->fun();
    t
->fun();
    Test
* k = new Test();
    k
->fun();                //这里第一输出就变成了9
}

// ★静态数据成员可以成为成员函数的可选参数,而普通数据成员则不可以。
// c6.c
#include  < iostream.h >

class  Test
{
    
static int s;
    
int a;
public:
    
int fun(int i = s)        //静态数据成员可以成为成员函数的可选参数
    {
        
return i;
    }

    
//int fun1(int i = a)    //这个是当然不行了
    
//{
    
//    return i;
    
//}
}
;
int  Test::s  =   5 ;

void  main()
{
    Test
* t = new Test();
    cout
<<t->fun()<<endl;
}


// C#语言事例
// (1)用于对静态字段、只读字段等的初始化。               
// (2)添加static关键字,不能添加访问修饰符,因为静态构造函数都是私有的。         
// (3)类的静态构造函数在给定应用程序域中至多执行一次:只有创建类的实例或者引用类的任何静态成员才激发静态构造函数 
// (4)静态构造函数是不可继承的,而且不能被直接调用。             
// (5)如果类中包含用来开始执行的 Main 方法,则该类的静态构造函数将在调用 Main 方法之前执行。     
//     任何带有初始值设定项的静态字段,则在执行该类的静态构造函数时,先要按照文本顺序执行那些初始值设定项。   
// (6)如果没有编写静态构造函数,而这时类中包含带有初始值设定的静态字段,那么编译器会自动生成默认的静态构造函数。
using  System;
class  Test 

    
static void Main() {
        Console.WriteLine(
"{0} {1}", B.Y, A.X);
    }

    
public static int F(string s) {
        Console.WriteLine(s);
        
return 1;
    }

}

class  A
{
    
public static int X = Test.F("Init A");
}

class  B
{
    
public static int Y = Test.F("Init B");
}

// 或者产生如下输出:
// Init A
// Init B
// 1 1
// 或者产生如下输出:
// Init B
// Init A
// 1 1
// 这是因为 X 的初始值设定项和 Y 的初始值设定项的执行顺序无法预先确定,上述两种顺序都有可能发生;
// 唯一能够确定的是:它们一定会在对那些字段的引用之前发生。但是,下面的示例:
using  System;
class  Test
{
    
static void Main() {
        Console.WriteLine(
"{0} {1}", B.Y, A.X);
    }

    
public static int F(string s) {
        Console.WriteLine(s);
        
return 1;
    }

}

class  A
{
    
static A() {}
    
public static int X = Test.F("Init A");
}

class  B
{
    
static B() {}
    
public static int Y = Test.F("Init B");
}

// 所产生的输出必然是:
// Init B
// Init A
// 1 1
// 这是因为关于何时执行静态构造函数的规则规定:
// B 的静态构造函数(以及 B 的静态字段初始值设定项)必须在 A 的静态构造函数和字段初始值设定项之前运行


有的东西你天天在用,但未必就代表你真正了解它,正如我之前所了解的 static 。

一、静态类

  静态类与非静态类的重要区别在于静态类不能实例化,也就是说,不能使用 new 关键字创建静态类类型的变量。在声明一个类时使用static关键字,具有两个方面的意义:首先,它防止程序员写代码来实例化该静态类;其次,它防止在类的内部声明任何实例字段或方法。

  1、静态类的主要特性:

  [1] 仅包含静态成员。

  [2] 无法实例化。

  [3] 静态类的本质,是一个抽象的密封类,所以不能被继承,也不能被实例化。

  [4] 不能包含实例构造函数。

  [5] 如果一个类下面的所有成员,都需要被共享,那么可以把这个类定义为静态类。

  2、静态类与私有构造函数区别:

  [1] 私有构造器方式仍然可以从类的内部对类进行实例化,而静态类禁止从任何地方实例化类,其中包括从类自身内部。

  [2] 使用私有构造器的类中,是允许有实例成员的,编译器不允许静态类有任何实例成员。

  [3] 使用静态类的优点在于,编译器能够执行检查以确保不致偶然地添加实例成员,编译器将保证不会创建此 类的实例。

  [4] C#编译器会自动把它标记为sealed。这个关键字将类指定为不可扩展;换言之,不能从它派生出其他类。

二、静态变量

  1、通过static关键字修饰,是属于类,实例成员属于对象,在这个类第一次加载的时候,这个类下面的所有静态成员会被加载。

  2、静态成员只被创建一次,所以静态成员只有一份,实例成员有多少个对象,就有多少份。

  3、类加载的时候,所有的静态成员就会被创建在“静态存储区”里面,一旦创建直到程序退出,才会被回收。

  4、变量需要被共享的时候,方法需要被反复调用的时候,就可以把这些成员定义为静态成员。

  5、在静态方法中,不能直接调用实例成员,因为静态方法被调用的时候,对象还有可能不存在。

  6、this/base 关键字在静态方法中不能使用,因为有可能对象还不存在。

  7、可以创建这个类的对象,制定对象的成员在静态方法中操作。

  8、在实例方法中,可以调用静态成员,因为这个时候静态成员肯定存在。

  9、非静态类可以包含静态的方法、字段、属性或事件;
  10、无论对一个类创建多少个实例,它的静态成员都只有一个副本;
  11、静态方法和属性不能访问其包含类型中的非静态字段和事件,并且不能访问任何对象的实例变量;
  12、静态方法只能被重载,而不能被重写,因为静态方法不属于类的实例成员;
  13、虽然字段不能声明为 static const,但 const 字段的行为在本质上是静态的。这样的字段属于类,不属于类的实例。

三、静态方法

  1、静态方法是不属于特定对象的方法;

  2、静态方法可以访问静态成员变量;

  3、静态方法不可以直接访问实例变量,可以在实例函数调用的情况下,实例变量做为参数传给静态方法;

  4、静态方法也不能直接调用实例方法,可以间接调用,首先要创建一个类的实例,然后通过这一特定对象来调用静态方法。

四、静态构造函数

  1、静态类可以有静态构造函数,静态构造函数不可继承;
  2、静态构造函数可以用于静态类,也可用于非静态类;
  3、静态构造函数无访问修饰符、无参数,只有一个 static 标志;
  4、静态构造函数不可被直接调用,当创建类实例或引用任何静态成员之前,静态构造函数被自动执行,并且只执行一次。

  例如:

复制代码
class Program
 {
         public static int i =0;
         public Program()
         {
             i = 1;
             Console.Write("实例构造方法被调用");
         }
         static Program()
         {
             i = 2;
             Console.Write("静态构造函数被执行");
         }
         static void Main(string[] args)
         {
             Console.Write(Program.i);//结果为2,首先,类被加载,所有的静态成员被创建在静态存储区,i=0,接着调用了类的成员,这时候静态构造函数就会被调用,i=2
             Program p = new Program();
             Console.Write(Program.i);//结果为1,实力化后,调用了实例构造函数,i=1,因为静态构造函数只执行一次,所以不会再执行。
         }
 }
复制代码

 

五、静态变量的存储

  使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员static修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。

静态全局变量

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

特点:A、该变量在全局数据区分配内存。   B、初始化:如果不显式初始化,那么将被隐式初始化为0。

 

静态局部变量

定义:在局部变量前加上static关键字时,就定义了静态局部变量。

特点:   A、该变量在全局数据区分配内存。   B、初始化:如果不显式初始化,那么将被隐式初始化为0。   C、它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或 语句块结束时,其作用域随之结束。

静态数据成员
特点
:   A、内存分配:在程序的全局数据区分配。   B、初始化和定义:     a、静态数据成员定义时要分配空间,所以不能在类声明中定义。     b、为了避免在多个使用该类的源文件中,对其重复定义,所在,不能在类的头文件中     定义。     c、静态数据成员因为程序一开始运行就必需存在,所以其初始化的最佳位置在类的内部实现。   C、特点     a、对相于 public,protected,private 关键字的影响它和普通数据成员一样,     b、因为其空间在全局数据区分配,属于所有本类的对象共享,所以,它不属于特定的类对象,在没产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它。
  D、访问形式     a、 类对象名.静态数据成员名

        E、静态数据成员,主要用在类的所有实例都拥有的属性上。比如,对于一个存款类,帐号相对   于每个实例都是不同的,但每个实例的利息是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局区的内存,所以节省存贮空间。第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了,因为它们实际上是共用一个东西。  

静态成员函数
特点
:   A、静态成员函数与类相联系,不与类的对象相联系。   B、静态成员函数不能访问非静态数据成员。原因很简单,非静态数据成员属于特定的类实例。
作用:   主要用于对静态数据成员的操作。

调用形式:   A、类对象名.静态成员函数名()

 

static静态变量的实例与分析

 实例

复制代码
using System;
namespace teststatic
{
    class class1
    {
        static int i = getNum();
        int j = getNum();
 
        static int num = 1;
        
        static int getNum()
        {
            return num;
        }
 
        static void Main(string[] args)
        {
            Console.WriteLine("i={0}",i);
            Console.WriteLine("j={0}", new class1().j);
            Console.Read();
        }
    }
}
复制代码

 

 

现在分析上面的代码

Console.WriteLine(string.Format("i={0}",i)); 这里i是static变量,而且类class1是第一次被引 用,要先为class1里面所有的static变量分配内存。尽管现在有超线程技术,但是指令在逻辑还是一条一条的按顺序执行的,所以 先为static int i分配内存,并且在该内存中保持int的缺省值0,接着再为static int num 变量分配内存,值当然也为0。

然后执行第二步,为变量赋值:先为static int i变量赋值,i=getNum(),看getNum里面的代码,就是return num,这个时候num的值是0,于是i就为0了。然后对变量num赋值,num=1;这行代码执行后,num就为1了。

所以最后的结果为:

 i=0 j=1

 

  首先分为两部分 寄存器和内存(包括缓存)

  内存分为两部分 代码和数据

  数据分为两部分 静态存储区和运行时存储

  运行时存储分为 堆栈 和 堆
  静态存储分为 全局静态存储 和 常量


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值