zoukankan      html  css  js  c++  java
  • C++ 中间statickeyword

            static顾名思义是一个静态的含义。在此我想谈谈相关的系统statickeyword角色,当然,这主要是对语言的发展C与C++角色,在信息方面的作用等方面,请另找。在评论中肯定有不恰当,请大胆地抛砖。手软,文中的内容引用了不少网上的资料。

            static从宏观上讲主要有两种使用方法,一、是面向过程设计;二是面向对象设计。前主要是涉及变量与函数的使用方法。后者呢包括前者使用方法的同一时候。还有在类中的使用方法。

    一、 面向过程设计中的static(C语言)

    在讲面向过程设计中的static使用方法时先搞点插曲,从历史上讲。C程序一直由以下几部分组成:


    正文段

            CPU运行的机器指令部分。通常,正文段是可共享的。所以即使是经常环境指针环境表环境字符串运行的程序(如文本编辑程序、C编译程序、s h e l l等)在存储器中也仅仅需有一个副本,另外,正文段经常是仅仅读的,以防止程序因为意外事故而改动其自身的指令。

    初始化数据段

            通常将此段称为数据段,它包括了程序中需赋初值的变量。初始化 的全局变量和 静态变量存放在这里。

    比如。C程序中不论什么函数之外的说明:int maxcount = 99; 使此变量以初值存放在初始化数据段中。

    a.初始化的全局变量

    b.初始化的静态变量

    非初始化数据段

           通常将此段称为bss段,这一名称来源于早期汇编程序的一个操作符,意思是“block started by symbol(由符号開始的块)”,未初始化的全局变量 和静态变量存放在这里。在程序開始运行之前。内核将此段初始化为0。函数外的说明:long sum[1000] ; 使此变量存放在非初始化数据段中。

    a.未初始化的全局变量

    b.未初始化的静态变量

            须要由程序猿分配释放管理,若程序猿不释放,程序结束时可能由OS回收。

    通常在堆中进行动态存储分配。

    如程序中的malloc, calloc,realloc等函数都从这里面分配。堆是从下向上分配的。

            由编译器自己主动分配释放管 理。

    局部变量及每次函数调用时返回地址、以及调用者的环境信息(比如某些机器寄存器)都存放在栈中。新被调用的函数在栈上为其自己主动和暂时变量分配存储空间。通过以这样的方式使用栈。C函数能够递归调用。

    递归函数每次调用自身时,就使用一个新的栈帧。因此一个函数调用实例中的变量绝不会影响还有一个函数调用实例中的变量。

    a.局部变量

    b.函数调用时返回地址

    c.调用者的环境信息(比如某些机器寄存器)

           在这我先把static的内部机制与优势先提到前面来讲述,本来想放到面向对象设计中来讲,可是它的重要性改变了初衷:

    static的内部机制:

            静态数据成员要在程序一開始执行时就必须存在。

    由于函数在程序执行中被调用,所以静态数据成员不能在不论什么函数内分配空间和初始化。

            这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现。那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。

            静态数据成员要实际地分配空间,故不能在类的声明中定义(仅仅能声明数据成员)。类声明仅仅声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以在类声明中写成定义是错误的。它也不能在头文件里类声明的外部定义,由于那会造成在多个使用该类的源文件里。对其反复定义。

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

    static的优势:

            能够节省内存,由于它是全部对象所公有的。因此,对多个对象来说。静态数据成员仅仅存储一处,供全部对象共用。静态数据成员的值对每一个对象都是一样。但它的值是能够更新的。

    仅仅要对静态数据成员的值更新一次,保证全部对象存取更新后的同样的值。这样能够提高时间效率。

    static局部变量

             静态局部变量属于静态存储方式。它具有下面特点:

            (1)静态局部变量 在函数内定义它的生存期 整个程序生命周期,可是其 作用域仍与 自己主动变量同样 ,仅仅能在定义该变量的函数内使用该变量。

    退出该函数后,虽然该变量还继续存在。但不能使用它。

           (2)对基本类型的静态局部变量若在声明时未赋以初值,则系统自己主动赋予0值 。

    而对自己主动变量不赋初值,则其值是不定的。

            依据静态局部变量的特点,能够看出它是一种生存期为整个程序生命周期。尽管离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用。并且保存了前次被调用后留下的值。因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑採用静态局部变量。尽管用全局变量也能够达到上述目的。但全局变量有时会造成意外的副作用。因此仍以採用局部静态变量为宜。

    #include <stdio.h>
    #include <stdlib.h>
     
    void Test()
    {
        static int tmpValue = 0;
        printf("value = %d
    ", ++tmpValue);
    }
     
    int main()
    {
        for(int index = 0; index < 50; ++index)
        {
            Test()
        }
        getchar();
        return 0;
    }

    全局变量

           全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。

    全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。
    这两者的差别在于:

    (1). 非静态全局变量作用域是整个源程序 ,当一个源程序由多个源文件组成时。非静态的全局变量在各个源文件里都是有效的。

    (2). 而静态全局变量 则限制了其作用域,即仅仅在定义该变量的源文件内有效。在同一源程序的其他源文件里不能使用它。

    因为静态全局变量的作用域局限于 一个源文件内 ,仅仅能为该源文件内的函数公用, 因此能够避免在其他源文件里引起错误。

    从以上分析能够看出,把全局变量改变为静态变量后是改变了它的作用域。限制了它的使用范围

    static 函数

             假设在一个源文件里定义的函数,仅仅能被本文件里的函数调用。而不能被同一程序其他文件里的函数调用,这样的函数称为static函数与称为静态函数。


    定义一个static函数。仅仅需在函数类型前再加一个“static”keyword就可以。例如以下所看到的:

    static 函数类型 函数名(函数參数表) {……}

    keyword“static”。译成中文就是“静态的”,所以内部函数又称静态函数。

    但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件

    使用内部函数的优点是:不同的人编写不同的函数时。不用操心自定义的函数。是否会与其他文件里的函数同名,由于同名也没有关系

    二、面向对象设计中的static(C++语言)

    static 数据成员

    在类内数据成员的声明前加上keywordstatic,该数据成员就是类内的静态数据成员。

    先举一个静态数据成员的样例。

    #include <stdio.h>
    #include <stdlib.h>
     
    class Test
    {
    public:
        Test() {}
        ~Test() {}
       
        Test(const Test& t)
        {
            if(this != &t)
            {
                this->m_nTest1 = t.m_nTest1;
                this->m_nTest2 = t.m_nTest2;
                this->m_nTest3 = t.m_nTest3;
            }
        }
       
        Test& operator=(const Test& t)
        {
            if(this != &t)
            {
                this->m_nTest1 = t.m_nTest1;
                this->m_nTest2 = t.m_nTest2;
                this->m_nTest3 = t.m_nTest3;
            }
            return *this;
        }
       
        void PrintOut()
        {
            //printf("test_1 = %d test_2 = %d test_3 = %d
    ",m_nSTest1, m_nSTest2, m_nSTest3);//错误
            printf("test_1 = %d test_2 = %d test_3 = %d
    ",m_nTest1, m_nTest2, m_nTest3);
        }
       
        static void PrintStatic()
        {
            printf("test_1 = %d test_2 = %d test_3 = %d
    ",m_nSTest1, m_nSTest2, m_nSTest3);
            //printf("test_1 = %d test_2 = %d test_3 = %d
    ",m_nTest1, m_nTest2, m_nTest3);//错误
        }
     
    public:
        int m_nTest1;
        static int m_nSTest1;
         
    protect:
        int m_nTest2;
        static int m_nSTest2;
       
    private:
        int m_nTest3;
        static int m_nSTest3;
    };
    int Test::m_nSTest1 = 10;
    int Test::m_nSTest2 = 20;
    int Test::m_nSTest3 = 30;
     
     
    int Test::m_nSTest1 = 10;
    int main()
    {
        Test t;
        t.PrintOut();
        //t.PrintStatic();//错误
        Test::PrintStatic();
        getchar();
        return 0;
    }


    static数据成员有下面特点:

           (1). 对于非static数据成员。每一个类对象都有自己的拷贝。而static数据成员被当作是类的成员。不管这个类的对象被定义了多少个,静态数据成员在程序中也仅仅有一份拷贝 ,由该类型的全部对象共享訪问。也就是说。静态数据成员是该类的全部对象所共同拥有的。

    对该类的多个对象来说,静态数据成员仅仅分配一次内存,供全部对象共用。

           (2). 静态数据成员存储在全局数据区。

    静态数据成员定义时才分配空间,所以不能在类声明中定义。在上例中。语句int Test::m_nSTest1= 10;是定义静态数据成员。

           (3). 静态数据成员和普通数据成员一样遵从public,protected,private訪问规则。

           (4). 由于静态数据成员在全局数据区分配内存。属于本类的全部对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时。我们就能够操作它;

           (5). 静态数据成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式为:<数据类型><类名>::<静态数据成员名>=<值> 如:int Test::m_nSTest1 = 10;

            (6). 类的静态数据成员有两种訪问形式:<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>

            (7). 静态数据成员主要用在各个对象都有同样的某项属性的时候。比方对于一个存款类,每一个实例的利息都是同样的。所以,应该把利息设为存款类的静态数据成员。这有两个优点,第一。无论定义多少个存款类对象。利息数据成员都共享分配在全局数据区的内存。所以节省存储空间。第二,一旦利息须要改变时,仅仅要改变一次,则全部存款类对象的利息全改变过来了;

            (8). 同全局变量相比。使用静态数据成员有两个优势:

    a. 静态数据成员没有进入程序的全局名字空间。因此不存在与程序中其他全局名字冲突的可能性;

    b. 能够实现信息隐藏。静态数据成员能够是private成员,而全局变量不能。

    static成员函数

    #include <stdio.h>
    #include <stdlib.h>
     
    class Test
    {
    public:
        Test() {}
        ~Test() {}
       
        Test(const Test& t)
        {
            if(this != &t)
            {
                this->m_nTest1 = t.m_nTest1;
                this->m_nTest2 = t.m_nTest2;
                this->m_nTest3 = t.m_nTest3;
            }
        }
       
        Test& operator=(const Test& t)
        {
            if(this != &t)
            {
                this->m_nTest1 = t.m_nTest1;
                this->m_nTest2 = t.m_nTest2;
                this->m_nTest3 = t.m_nTest3;
            }
            return *this;
        }
       
        void PrintOut()
        {
            //printf("test_1 = %d test_2 = %d test_3 = %d
    ",m_nSTest1, m_nSTest2, m_nSTest3);//错误
            printf("test_1 = %d test_2 = %d test_3 = %d
    ",m_nTest1, m_nTest2, m_nTest3);
        }
       
        static void PrintStatic()
        {
            printf("test_1 = %d test_2 = %d test_3 = %d
    ",m_nSTest1, m_nSTest2, m_nSTest3);
            //printf("test_1 = %d test_2 = %d test_3 = %d
    ",m_nTest1, m_nTest2, m_nTest3);//错误
        }
     
    public:
        int m_nTest1;
        static int m_nSTest1;
         
    protect:
        int m_nTest2;
        static int m_nSTest2;
       
    private:
        int m_nTest3;
        static int m_nSTest3;
    };
    int Test::m_nSTest1 = 10;
    int Test::m_nSTest2 = 20;
    int Test::m_nSTest3 = 30;
     
     
    int Test::m_nSTest1 = 10;
    int main()
    {
        Test t;
        t.PrintOut();
        //t.PrintStatic();//错误
        Test::PrintStatic();
        getchar();
        return 0;
    }

           static 成员函数,它为类的所有服务而不是为某一个类的详细对象服务。普通的成员函数一般都隐含了一个this指针,由于普通成员函数总是详细的属于某个类的详细对象的。

    通常情况下,this是缺省的。

    如函数fn()实际上是this->fn()。可是与普通函数相比,静态成员函数由于不是与不论什么的对象相联系。因此它不具有this指针 。从这个意义上讲。它无法訪问属于类对象的no-static数据成员。也无法訪问no-static成员函数。它仅仅能调用其余的静态成员函数 。

    关于静态成员函数,能够总结为下面几点:

    (1). 出如今类体外的函数定义不能指定keywordstatic

    (2). static成员之间能够相互訪问 ,包含static成员函数訪问static数据成员和訪问static成员函数;

    (3). 非静态成员函数能够随意地訪问静态成员函数和静态数据成员;

    (4). 静态成员函数不能訪问非静态成员函数和非静态数据成员,仅仅能訪问静态的

    (5). 因为没有this指针的额外开销。因此静态成员函数与类的全局函数相比速度上会有少许的增长;

    (6). 调用静态成员函数。能够用成员訪问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也能够直接使用例如以下格式:

    <类名>::<静态成员函数名>(<參数表>)

    如:Test::PrintStatic(),调用类的静态成员函数。

    可是,一样要遵从public,protected,private訪问规则。


    附 public,protected,private訪问规则

    第一:private, public, protected訪问标号的訪问范围。

    private:仅仅能由1.该类中的函数、2.其友元函数訪问。

    不能被不论什么其它訪问。该类的对象也不能訪问。

    protected:能够被1.该类中的函数、2.子类的函数、以及3.其友元函数訪问。

    但不能被该类的对象訪问。

    public:能够被1.该类中的函数、2.子类的函数、3.其友元函数訪问,也能够由4.该类的对象訪问。

     

    注:友元函数包含3种:设为友元的普通的非成员函数;设为友元的其它类的成员函数;设为友元类中的全部成员函数。

    第二:类的继承后方法属性变化。

    private属性不可以被继承。

    使用private继承,父类的protected和public属性在子类中变为private;
    使用protected继承,父类的protected和public属性在子类中变为protected;
    使用public继承,父类中的protected和public属性不发生改变;

    例如以下所看到的:

                         public:           protected:       private:

    public继承            public            protected       不可用 

    protected继承         protected         protected       不可用 

    private继承           private          private          不可用

    protected继承和private继承能减少訪问权限。

    为了进一步理解三种不同的继续方式在其成员的可见性方面的差别,以下从三种不同角度进行讨论。
      对于公有继续方式:
      (1) 基类成员对其对象的可见性:
      公有成员可见,其它不可见。这里保护成员同于私有成员。
      (2) 基类成员对派生类的可见性:
      公有成员和保护成员可见,而私有成员不可见。

    这里保护成员同于公有成员。


      (3) 基类成员对派生类对象的可见性:
      公有成员可见。其它成员不可见。


      所以,在公有继续时。派生类的对象能够訪问基类中的公有成员。派生类的成员函数能够訪问基类中的公有成员和保护成员。这里,一定要区分清楚派生类的对象和派生类中的成员函数对基类的訪问是不同的。
      对于私有继续方式:
      (1) 基类成员对其对象的可见性:
      公有成员可见,其它成员不可见。


      (2) 基类成员对派生类的可见性:
      公有成员和保护成员是可见的,而私有成员是不可见的。
      (3) 基类成员对派生类对象的可见性:
      全部成员都是不可见的。


      所以,在私有继续时,基类的成员仅仅能由直接派生类訪问。而无法再往下继续。
      对于保护继续方式:
      这样的继续方式与私有继续方式的情况同样。两者的差别仅在于对派生类的成员而言,对基类成员有不同的可见性。
      上述所说的可见性也就是可訪问性。关于可訪问性还有另的一种说法。

    这样的规则中,称派生类的对象对基类訪问为水平訪问。称派生类的派生类对基类的訪问为垂直訪问。
      一般规则例如以下:
      公有继续时,水平訪问和垂直訪问对基类中的公有成员不受限制;
      私有继续时,水平訪问和垂直訪问对基类中的公有成员也不能訪问;
      保护继续时。对于垂直訪问同于公有继续,对于水平訪问同于私有继续。


      对于基类中的私有成员。仅仅能被基类中的成员函数和友元函数所訪问。不能被其它的函数訪问。
      基类与派生类的关系
      不论什么一个类都能够派生出一个新类,派生类也能够再派生出新类。因此。基类和派生类是相对而言的。
      基类与派生类之间的关系能够有例如以下几种描写叙述:
      1. 派生类是基类的详细化
      类的层次通常反映了客观世界中某种真实的模型。在这样的情况下。不难看出:基类是对若干个派生类的抽象,而派生类是基类的详细化。基类抽取了它的派生类的公共特征,而派生类通过添加行为将抽象类变为某种实用的类型。
      2. 派生类是基类定义的延续
      先定义一个抽象基类,该基类中有些操作并未实现。然后定义非抽象的派生类,实现抽象基类中定义的操作。

    比如,虚函数就属此类情况。这时。派生类是抽象的基类的实现。就可以看成是基类定义的延续。

    这也是派生类的一种经常用法。
      3. 派生类是基类的组合
      在多继续时。一个派生类有多于一个的基类。这时派生类将是全部基类行为的组合。


      派生类将其本身与基类差别开来的方法是加入数据成员和成员函数。因此,继续的机制将使得在创建新类时,仅仅需说明新类与已有类的差别。从而大量原有的程序代码都能够复用,所以有人称类是“可重用的软件组件”。


    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    A1066 Root of AVL Tree (25 分)
    A1099 Build A Binary Search Tree (30 分)
    A1043 Is It a Binary Search Tree (25 分) ——PA, 24/25, 先记录思路
    A1079; A1090; A1004:一般树遍历
    A1053 Path of Equal Weight (30 分)
    A1086 Tree Traversals Again (25 分)
    A1020 Tree Traversals (25 分)
    A1091 Acute Stroke (30 分)
    A1103 Integer Factorization (30 分)
    A1032 Sharing (25 分)
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4678794.html
Copyright © 2011-2022 走看看