C++static详解,类中的static用法说明

2022-11-13 12:11:03 详解 用法 类中

C++static详解,类中static用法

static特点:用来控制存储方式和可见性

① 存储空间:静态存储区(控制变量的存储方式)

静态变量存储在静态存储区(存储在静态存储区的变量,如果不显式地对其进行初始化,系统会将其初始化为0),在程序执行期间,对应的存储空间不会释放,一直到程序结束才会释放。

static控制变量的存储方式体现在局部变量上。局部变量存储在动态存储区,在局部变量定义前加上static,该局部变量就变成了局部静态变量,局部静态变量存储在静态存储区,即使函数调用结束,它占用的存储空间也不会释放,但其他函数不能引用该局部静态变量。当下一次调用该函数时,不会再次对该局部静态变量进行初始化,它的值是上一次函数调用结束时的值。

对全局变量而言,存储方式没有什么改变,因为全局变量和全局静态变量都存储在静态存储区。

② 作用域:(控制变量、函数的可见性)

static控制变量的可见性体现在全局静态变量和静态函数上。

全局变量默认具有外部链接性,作用域是整个工程。使用static关键字可以限制全局变量的作用域,全局静态变量的作用域仅限本文件,它对在其他文件不可见,也就是说不能在其他文件中引用该全局静态变量,但其他文件中可以定义和它名字相同的变量,不会发生冲突。

在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数的不同在于,它只能在声明它的文件当中可见,不能被其它文件使用,其它文件中可以定义相同名字的函数,不会发生冲突。

局部静态变量的作用域与局部变量的作用域相同,其作用域都是从定义开始到函数或程序块结束为止。

类中的static关键字

在类中声明static变量或者函数时,初始化时使用作用域运算符(::)来标明它所属类,静态成员是类的成员(所有对象中共享的成员),而不是某一个对象的成员。

① 静态数据成员

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

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

对于非静态数据成员,每个对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只会被分配一次内存,供所有对象共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新。

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

同全局变量相比,使用静态数据成员有两个优势:

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

可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;

② 静态成员函数

与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部服务而不是为某一个类的具体对象服务。静态成员函数与静态数据成员一样,都是类的内部实现,属于类定义的一部分。普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。通常情况下,this是缺省的,如函数fun()实际上是this->fun()。但是与普通函数相比,静态成员函数由于不与任何的对象相联系,因此它不具有this指针。

非静态成员函数可以任意地访问静态成员函数和静态数据成员;

静态成员函数不能访问非静态成员函数和非静态数据成员;静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;

什么时候用static?

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

为什么要引入static?

函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,大家知道,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题:如果想将函数中此变量的值保存至下一次调用时,如何实现?最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此函数控制)。

c++中static总结

经过static修饰的变量,存储在内存的全局静态区。且被static修饰的变量只能在本模块的所有函数引用。

内存中的存储区域如下:

  • 堆区:是由程序员手动申请(new)与释放(delete)的内存区域。从低地址向高地址申请;内存空间大、存储地址不连续,一般是链式的;速度较慢。
  • 栈区:由编译器自动分配和释放,主要存储 函数的参数值、函数内部的变量的值、函数调用的空间。从高地址向低地址申请;容量有限;速度较快;存储地址连续,会溢出。
  • 代码区:又叫文本段(.text),存放着程序的机器代码,可执行指令就是存储在这里的,这里的代码是只读的。
  • 全局区(静态区):全局变量和静态变量是存储在这里的。初始化的全局变量和静态变量在一块区域(.data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bbs)。系统结束后由系统释放。
  • 常量区:常量字符串放在这里,程序结束后,由系统进行释放。

1. 概念

static的用法主要体现在两个方面:面向过程中的static和面向对象中的static。

面向过程的static主要包括静态全局变量、静态局部变量和静态函数。

面向对象的static主要包括静态成员变量、静态成员函数。

2. 面向过程的static

2.1 静态全局变量

全局变量前添加static关键字,则该变量称为静态全局变量。

#include <iOStream>
#include <stdio.h>
 
static int a = 10;
 
void Func()
{
    a++;
}
 
int main()
{
    printf("a = %d\n", a);//输出:10
    Func();//输出:10
    printf("a = %d\n", a);//输出:11
 
    system("pause");
    return 0;
}

特点:

1)在全局数据中的变量如果没有显示的初始化会自动被程序初始化为0(这个特性非静态全局变量也有),而在函数体内声明的变量如果不显示初始化则会使一个随机值;

2)静态全局变量在声明它的整个文件中都是可见的,而在文件之外是不可见的;

3)静态全局变量在全局数据区分配内存;

4)其他文件中可以定义同名int型变量a,不会冲突;

如果将static去掉,具有以下特点:

1)全局变量默认是有外部连接性的,其作用域是整个工程,在一个文件内定义的全局变量可以通过包含其所在头文件或显示调用 extern关键字修饰全局变量的变量名声明来引用;

2)静态全局变量是显示调用static修饰的全局变量,其作用域只在声明此变量的文件中,其他文件即使使用extern关键字修饰其声明也不可使用;

2.2 静态局部变量

局部变量前添加static关键字,则该变量称为静态局部变量。

#include <iostream>
#include <stdio.h>
 
void Func()
{
    static int a = 5;
    printf("a = %d\n", a);
    a++;
}
 
int main()
{
    for (int i = 0; i < 5; i++)
    {
        Func();  //打印结果:5 6 7 8 9
    }
 
    system("pause");
    return 0;
}

通常,在一个函数作用域内定义一个变量,每次运行到该函数时,系统会给局部变量分配内存。当函数结束时,该变量的内存会被系统回收至栈内存当中。

特点:

1)内存存放在程序的全局数据区中;

2)静态局部变量在程序执行到该对象声明时,会被首次初始化。其后运行到该对象的声明时,不会再次初始化,这也是为什么上面程序测试函数每次输出的值都是递增的原因(只会被初始化一次);

3)如果静态局部变量没有被显式初始化,则其值会自动被系统初始化为0;

4)局部静态变量不能被其作用域之外的其他模块调用,其调用范围仅限于声明该变量的函数作用域当中;

2.3 静态函数

函数返回类型前添加static关键字,则该变量称为静态函数。

#include <iostream>
#include <stdio.h>
 
static void Func()
{
    printf("This is a static function\n");
}
 
int main()
{
    Func(); 
    
    system("pause");
    return 0;
}

特点:

1)作用域只在声明它的文件当中,不能被其他文件引用,其他文件可以定义同名的全局函数;

2)其他文件想要调用本文件的静态函数,需要显示的调用extern关键字修饰其声明;

3. 面向对象中的static 

3.1 静态成员变量

#include <iostream>
#include <stdio.h>
using namespace std;
 
class Test
{
public:
    Test(int a, int b, int c) : 
        m_a(a),
        m_b(b),
        m_c(c)
    {
        m = a + b + c;
    }
 
    void Show()
    {
        cout << "m = " << m << endl;
    }
 
private:
    int m_a, m_b, m_c;
    static int m;
};
 
int Test::m = 0; //初始化静态数据成员
 
int main()
{
    Test ClassA(1, 1, 1);
    ClassA.Show();  //输出: 3
    Test ClassB(3, 3, 3);
    ClassB.Show(); //输出: 9
    ClassA.Show(); //输出: 9
 
    system("pause");
    return 0;
}

特点:

1)静态数据成员的服务对象并非是单个类实例化的对象,而是所有类实例化的对象(这点可以用于设计模式中的单例模式实现);

2)静态数据成员必须显式的初始化分配内存,在其包含类没有任何实例化之前已经有内存分配;

3)静态数据成员与其他成员一样,遵从public,protected,private的访问规则;

4)静态数据成员内存存储在全局数据区,只随着进程的消亡而消亡;

优势:

1)静态数据成员不进入程序全局命名空间,不会与其他全局名称的同名同类型变量冲突;

2)静态数据成员可以实现C++的封装特性,由于其遵守类的访问权限规则,所以相比全局变量更加灵活;

3.2 静态成员函数

类的成员函数返回类型之前添加static,此成员函数为静态成员函数。

#include <iostream>
#include <stdio.h>
using namespace std;
 
class Test
{
public:
    Test(int a, int b, int c) : 
        m_a(a),
        m_b(b),
        m_c(c)
    {
        m = a + b + c;
    }
 
    static void Show()
    {
        cout << "m = " << m << endl;
    }
 
private:
    int m_a, m_b, m_c;
    static int m;
};
 
int Test::m = 0; //初始化静态数据成员
 
int main()
{
    Test ClassA(1, 1, 1);
    ClassA.Show();
    Test ClassB(3, 3, 3);
    ClassB.Show();
    ClassA.Show();
 
    Test::Show(); //输出: 9
 
    system("pause"); 
    return 0;
}

特点:

1)静态成员函数比普通成员函数多了一种调用方式;

2)在没有实例化的类对象的条件下可以调用类的静态成员函数;

3)静态成员函数中没有隐含的this指针,所以静态成员函数不可以操作类中的非静态成员(由于第二条可知,类的非静态成员是在类实例化后存在的,而类的成员函数可以在类没有实例化的时候调用,故不能操作类的非静态成员);

4. 小结

1)静态数据成员都是静态存储的,所以必须在main函数之前显示的对其进行初始化;

2)不能再头文件中声明静态全局变量,原因是可能是产生了多个同名的静态数据;

3)不能将静态成员函数定义为虚函数;

4)静态成员函数没有this指针;

5)static缩短了子类对父类静态成员访问的时间,相对来说节省了内存空间;

6)如果不想在子类中操作父类的静态成员,则可以在子类中定义一个同名的static成员。这样既可覆盖父类中的静态成员,并且根据C++的多态性变量命名规则,这样做是安全的;

7)静态成员声明在类中,操作在其外部,所以对其取地址操作就跟取普通成员的操作略有不同。静态变量地址是指向其数据类型的指针,函数地址则是一个类型为nonmember的函数指针; 

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

相关文章