• C++ static 关键字详解


     
    一、预备知识—程序的内存分配
    一个由c/C++编译的程序占用的内存分为以下几个部分
    1、栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
    2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
    3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(.data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss)。 - 程序结束后由系统释放。
    4、文字常量区 —常量字符串就是放在这里的(.rodata)。 程序结束后由系统释放。
    5、程序代码区—存放函数体的二进制代码(.text)。
     
    二、例子程序
    这是一个前辈写的,非常详细
    //main.cpp
    int a = 0;          // 全局初始化区
    char *p1;           // 全局未初始化区
    main()
    {
      int b;            // 栈区
      char s[] = "abc"; // 栈区
      char *p2;         // 栈区
      char *p3 = "123456";     // "123456\0" 在常量区,p3在栈区
      static int c =0;         // 全局(静态)初始化区
     
      p1 = (char *)malloc(10);
      p2 = (char *)malloc(20); // 分配得来的10和20字节的区域就在堆区
     
      strcpy(p1, "123456");    // "123456\0" 放在常量区,编译器可能会将它
                                  // 与p3所指向的"123456"优化成一个地方。
     
     ============================================================== 
    static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
    static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?   
    :
    1) 
    全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
    2) 从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。                   
    3) static
    函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件   
    综上所述:
    static
    全局变量与普通的全局变量有什么区别:

    static全局变量只初使化一次,防止在其他文件单元中被引用  
    static局部变量和普通局部变量有什么区别:
    static局部变量只被初始化一次,下一次依据上一次结果值;   
    static函数与普通函数有什么区别:
    static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
    ------------------------

    不管是C,C++,还是JAVA,它们中都包含static。当然这其中的用法及作用也是不一样的。下面主要介绍static在C++中的用法:

    要理解static,就必须要先理解另一个与之相对的关键字,很多人可能都还不知道有这个关键字,那就是auto,其实我们通常声明的不用static修饰的变量,都是auto的,因为它是默认的,就象short和long总是默认为int一样;我们通常声明一个变量:

    int a;

    string s;

    其实就是:

    auto int a;

    auto string s;

    而static变量的声明是:

    static int a;

    static string s;

    这样似乎可以更有利于理解auto和static是一对成对的关键字吧,就像private,protected,public一样;
    对于static的不理解,其实就是对于auto的不理解,因为它是更一般的;有的东西你天天在用,但未必就代表你真正了解它;auto的含义是由程序自动控制变量的生存周期,通常指的就是变量在进入其作用域的时候被分配,离开其作用域的时候被释放;而static就是不auto,变量在程序初始化时被分配,直到程序退出前才被释放;也就是static是按照程序的生命周期来分配释放变量的,而不是变量自己的生命周期;所以,像这样的例子:

    void func()  
    {  
    int a;  
    static int b;  
    } 

      每一次调用该函数,变量a都是新的,因为它是在进入函数体的时候被分配,退出函数体的时候被释放,所以多个线程调用该函数,都会拥有各自独立的变量a,因为它总是要被重新分配的;而变量b不管你是否使用该函数,在程序初始化时就被分配的了,或者在第一次执行到它的声明的时候分配(不同的编译器可能不同),所以多个线程调用该函数的时候,总是访问同一个变量b,这也是在多线程编程中必须注意的!

    static的全部用法:

    1.类的静态成员:

    A.h

    class A

    private:

    static int s_value;

    };

    在cpp中必须对它进行初始化:

    int A::s_value = 0;// 注意,这里没有static的修饰

    fatal error LNK1169: 找到一个或多个多重定义的符号

    这个错误我找了好久才发现,原因应该把int A::s_value = 0放在cpp文件中,放在.h文件中将产生错误

    如果这句话放在A.h的最后,那么编译将产生问题:

      类的静态成员是该类所有实例的共用成员,也就是在该类的范畴内是个全局变量,也可以理解为是一个名为A::s_value的全局变量,只不过它是带有类安全属性的;道理很简单,因为它是在程序初始化的时候分配的,所以只分配一次,所以就是共用的;

      类的静态成员必须初始化,(初始化必须在类外,只有const static int i既可以在类内又可以在类外:

    如在类中定义;

    const static int xxx;

    我们可以在.cpp中定义:

    const   int A::xxx=0;(一定要有const,否则错误会是 重定义;不同的类型修饰符

    道理也是一样的,因为它是在程序初始化的时候分配的,所以必须有初始化,类中只是声明,在cpp中才是初始化,你可以在初始化的代码上放个断点,在程序执行main的第一条语句之前就会先走到那;如果你的静态成员是个类,那么就会调用到它的构造函数;

    (

    静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。

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

    )

    2.类的静态函数:

    class A  

    private:

    static void func(int value);

    };

    实现的时候也不需要static的修饰,因为static是声明性关键字(同friend,声明时只需在友元的名称前加上关键字friend);类的静态函数是在该类的范畴内的全局函数,不能访问类的私有成员,只能访问类的静态成员,不需要类的实例即可调用;实际上,它就是增加了类的访问权限的全局函数:

    void

    A::fun(int);

    静态成员函数可以继承和覆盖,但无法是虚函数;

    3.只在cpp内有效的全局变量:

    在cpp文件的全局范围内声明: 

    static int g_value = 0;

    这个变量的含义是在该cpp内有效,但是其他的cpp文件不能访问这个变量;如果有两个cpp文件声明了同名的全局静态变量,那么他们实际上是独立的两个变量;

    如果不使用static声明全局变量:

    int g_value = 0;

    那么将无法保证这个变量不被别的cpp共享,也无法保证一定能被别的cpp共享,因为要让多个cpp共享一个全局变量,应将它声明为extern(外部)的;也有可能编译会报告变量被重复定义;总之不建议这样的写法,不明确这个全局变量的用法;

    如果在一个头文件中声明:

    static int g_vaule = 0;

    那么会为每个包含该头文件的cpp都创建一个全局变量,但他们都是独立的;所以也不建议这样的写法,一样不明确需要怎样使用这个变量,因为只是创建了一组同名而不同作用域的变量;

    这里顺便说一下如何声明所有cpp可共享的全局变量,在头文件里声明为extern的:

    extern int g_value; // 注意,不要初始化值!

    然后在其中任何一个包含该头文件的cpp中初始化(一次)就好:

    int g_value = 0; // 初始化一样不要extern修饰,因为extern也是声明性关键字;

    然后所有包含该头文件的cpp文件都可以用g_value这个名字访问相同的一个变量;

    4.只在cpp内有效的全局函数:

    在cpp内声明: 

    static void func();

    函数的实现不需要static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;道理和如果不使用static会引起的问题和第3点一样;不要在头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则在cpp内部声明需要加上static修饰;在C语言中这点由为重要!

    总之,不管是面向过程程序设计中的static和还是面向对象程序设计中的static,只要遵循以上四点,相信这方面的困难都可以解决。

    static变量只有一次初始化,不管在类中还是在函数中..有这样一个函数:

    void Foo()
    {
        static int a=3; // initialize 
        std::cout << a;
        a++;
    }

    static int a=3只执行了一次。在main中调用Foo()两次,结果为34.将上面的函数改为

    void Foo()
    {
        static int a;
            a=3;           // not initialize
        std::cout << a;
        a++;
    }

    同样在Foo()中调用两次.结果为33

      在类中使用非const的static类成员变量。初始化时要使用typename classname::variablename = value的形式

    例如:

    class myClass
    {
    public:
        static int a;
        myClass()
        {
        }
    };
    int myClass::a = 3; // here initialize
    int main()
    {
           cout << myClass::a;
           return 0;
    }

    如果使用的是const类型的static变量,那么就要在类中初始化只有静态常量整型数据成员才可以在类中初始化

    class myClass
    {
    public:
        const static int a=3; // here initialize
        myClass()
        {
        }
    };

    如果是模板中使用非const的static的变量..那需要根据具体类型初始化。

    例如 int myClass<int>::a = 4;那么如果你调用的是cout << myClass<double>::a,那一定会编译出错的。

    因为模板是不是具体类型,myClass<int>, myClass<double>才是一个具体类型,而一个类静态成员在特定类中被初始化一次。这样就好理解了.

     看下面一个代码:

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<string>
     4 using namespace std;
     5 
     6 
     7 
     8 class Student{
     9 
    10 private:
    11     double midterm,final;
    12     string name;
    13 public:
    14      static int x;
    15 
    16     Student();
    17     void getMidterm() { cout<< midterm<<endl;}
    18     void   getFinal() { cout<<final; }
    19 
    20      
    21 
    22 
    23 };
    24 
    25 int Student::x=10;
    26 
    27 Student::Student():midterm(1),final(2) {   }
    28 
    29 int main()
    30 {
    31     //Student s;
    32      
    33     
    34   
    35     Student *s=new Student();
    36     s->getMidterm();
    37     s->getFinal();
    38     cout<<endl;
    39     cout<<s->x<<endl;
    40 }

    如果改成下面的一句代码:

     const static int x=20; 在常量静态成员是可以在类中定义的。还可以const static在类外也可以定义:

    #include<iostream>
    #include<algorithm>
    #include<string>
    using namespace std;
    
    
    
    class Student{
    
    private:
        double midterm,final;
        string name;
    public:
         const static int x;
         
    
        Student();
        void getMidterm() { cout<< midterm<<endl;}
        void   getFinal() { cout<<final; }
    
         
    
    
    };
    
    const int Student::x=10;
    
    Student::Student():midterm(1),final(2) {   }
    
    int main()
    {
        //Student s;
         
        
      
        Student *s=new Student();
        s->getMidterm();
        s->getFinal();
        cout<<endl;
        cout<<s->x<<endl;
    }

     

     

     

  • 相关阅读:
    python小白-day9 数据库操作与Paramiko模块
    python小白-day8 线程、进程、协程
    python小白-day8 socketserver模块
    python小白-day7 socket初识
    python小白-day7 面向对象高级部分
    python小白-day6 xml处理模块
    python小白-day6 ConfigParser模块
    2020软件定义网络实验二
    软件工程实践第一次个人作业
    2020软件定义网络实验一
  • 原文地址:https://www.cnblogs.com/youxin/p/2506757.html
Copyright © 2020-2023  润新知