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

目录
  • C++static详解,类中static用法
    • static特点:用来控制存储方式和可见性
    • 类中的static关键字
    • 什么时候用static?
    • 为什么要引入static?
  • c++中static总结
    • 1. 概念
    • 2. 面向过程的static
    • 3. 面向对象中的static
    • 4. 小结

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的函数指针;

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

(0)

相关推荐

  • C/C++ extern和static的使用详解

    目录 前言 extern static c++ static members in class 总结 前言 在讲到extern和static的时候先了解一下定义和声明的基本概念 定义(define): A variable is defined when the compiler allocates the storage for the variable,就是我们的变量个其存储的具体值相关联 声明(declared) 编译器声明这个变量的存在,宣告其类型但是并不关联某个存储的具体值 你可以声明

  • C++示例讲解friend static const关键字的用法

    目录 一.友元函数 1.1重载operator<< 1.2友元函数 1.3友元类 二.关键字const 2.1const修饰类的成员函数 三.关键字static 3.1static类成员 3.2面试题 总结 一.友元函数 1.1重载operator<< 问题:现在我们尝试去重载operator<<,然后发现我们没办法将operator<<重载成成员函数.因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置.this指针默认是第一个参数也就是左操

  • C++的static静态成员你有了解吗

    目录 一.静态成员 二.静态成员变量 三.静态成员函数 四.访问静态成员的方式 五.小结 六.静态成员必须在定义类的文件中对静态成员变量进行初始化 七.什么能在类内初始化 八.C++静态类型成员变量的初始化顺序 总结 一.静态成员 即在定义前面加上static关键字的成员. #include<iostream> using namespace std; class A{ public: A(int a, int b): m_a(a), m_b(b){ num += m_a + m_b; } ~

  • C/C++的关键字之static你了解吗

    目录 C语言 隐藏 场景演示 解决方法 保持变量内容的持久 默认初始化为0 Cpp static类成员变量 static类成员方法 单例模式 总结 C语言 隐藏 场景演示 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性.会导致符号表认为存在同名全局变量和函数发生碰撞. 场景:全局的变量/函数在.h中会在多个.cc文件中拥有且全局可见有链接问题. a.h #pragma once #include<stdio.h> void Test() { printf(&

  • C++单例模式为何要实例化一个对象不全部使用static

    C++的单例模式为什么不直接全部使用static,而是非要实例化一个对象? 通过getInstance()函数获取单例对象,这种模式的关键之处不是在于强迫你用函数来获取对象.关键之处是让static对象定义在函数内部,变成局部static变量. 看下这种实现方式的经典demo: class Singleton { public: static Singleton& getInstance() { static Singleton inst; return inst; } Singleton(co

  • C/C++中的static关键字详解

    目录 C/C++ 中的 static 1. 静态局部变量 2. 静态全局变量 3. static 修饰函数 C++的 static 成员 静态成员变量 静态成员函数 总结: static是 C/C++中的关键字之一,是常见的函数与变量(C++中还包括类)的修饰符,它常被用来控制变量的存储方式和作用范围. 在众多高级语言中都有其作为关键字或函数出现,所以这也是应当被程序员熟知其各种含义的一个单词 我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间

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

    目录 C++static详解,类中static用法 static特点:用来控制存储方式和可见性 类中的static关键字 什么时候用static? 为什么要引入static? c++中static总结 1. 概念 2. 面向过程的static 3. 面向对象中的static 4. 小结 C++static详解,类中static用法 static特点:用来控制存储方式和可见性 ① 存储空间:静态存储区(控制变量的存储方式) 静态变量存储在静态存储区(存储在静态存储区的变量,如果不显式地对其进行初始

  • 详解java中的static关键字

    Java中的static关键字可以用于修饰变量.方法.代码块和类,还可以与import关键字联合使用,使用的方式不同赋予了static关键字不同的作用,且在开发中使用广泛,这里做一下深入了解. 静态资源(静态变量与静态方法) 被static关键字修饰的变量和方法统一属于类的静态资源,是类实例之间共享的.被static关键字修饰的变量.方法属于类变量.类方法,可以通过[类名.变量名].[类名.方法名]直接引用,而不需要派生一个类实例出来. 静态资源分类存放的好处 JDK把不同的静态资源放在了不同的

  • 详解C++中mutable的用法

    代码编译运行环境:VS2017+Win32+Debug mutalbe的中文意思是"可变的,易变的",是constant(即C++中的const)的反义词.在C++中,mutable也是为了突破const的限制而设置的,被mutable修饰的变量将永远处于可变的状态. mutable的作用有两点: (1)保持常量对象中大部分数据成员仍然是"只读"的情况下,实现对个别数据成员的修改: (2)使类的const函数可以修改对象的mutable数据成员. 使用mutable

  • 详解python中静态方法staticmethod用法

    在开发的时候, 可以使用类对方法进行封装,如果某一个方法需要访问到对象的实例属性,可以把这个方法封装成一个实例方法.如果某一个方法不需要访问对象的实例属性,但是需要访问到类的类属性,这个时候就可以考虑把这个方法封装成一个类方法.一个实例方法, 一个类方法,这是两种方法类型,但是在开发中还有一种情况,如果要封装的某一个方法,既不需要访问到对象的实例属性,也不需要访问类的类属性,这个时候就可以考虑把这个方法封装成一个静态方法. 在开发中,如果类中的某个方法既不需要访问实例属性或者调用实例方法,同时也

  • 详解Reactor中Context的用法

    目录 一.使用介绍 二.源码解读 三.如何桥接现有的ThreadLocal系统 四.总结 在响应式编程中,多线程异步性成为天然的内在,多线程之间的切换也成为原生的,在处理一个数据流Flux/Mono时,基本无法知道是运行在哪个线程上或哪个线程池里,可以说,每一个操作符operator以及内部的函数都可能运行在不同的线程上.这就意味着,以前用ThreadLocal来作为方法间透明传递共享变量的方式不再行得通.为此,Reactor提供了Context来替代ThreadLocal实现一个跨线程的共享变

  • 一文详解Python中复合语句的用法

    目录 Python复合语句 1.if 语句 2.while 语句 3.for 语句 4.try 语句 5.with 语句 6.match 语句 Python复合语句 复合语句是包含其它语句(语句组)的语句:它们会以某种方式影响或控制所包含其它语句的执行.通常,复合语句会跨越多行,虽然在某些简单形式下整个复合语句也可能包含于一行之内. if.while和for语句用来实现传统的控制流程构造.try语句为一组语句指定异常处理和/和清理代码,而with语句允许在一个代码块周围执行初始化和终结化代码.函

  • 详解C# 中Session的用法

    Session模型简介 在学习之前我们会疑惑,Session是什么呢?简单来说就是服务器给客户端的一个编号.当一台WWW服务器运行时,可能有若干个用户浏览正在运正在这台服务器上的网站.当每 个用户首次与这台WWW服务器建立连接时,他就与这个服务器建立了一个Session,同时服务器会自动为其分配一个SessionID,用以标识这个用 户的唯一身份.这个SessionID是由WWW服务器随机产生的一个由24个字符组成的字符串,我们会在下面的实验中见到它的实际样子. 这个唯一的SessionID是有

  • 详解Golang中Channel的用法

    如果说goroutine是Go语言程序的并发体的话,那么channels则是它们之间的通信机制.一个channel是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息. 1 创建channel 每个channel都有一个特殊的类型,也就是channels可发送数据的类型.一个可以发送int类型数据 的channel一般写为chan int.使用内置的make函数,如果第二个参数大于0,则表示创建一个带缓存的channel. ch := make(chan in

  • 详解c++中的 static 关键字及作用

    注:若没有特指是 静态成员时,默认都是普通成员: 1 类中的普通成员 类中的成员变量 和 成员函数 是分开存储的.其中, 1)每个对象都有独立的成员变量:成员变量可以存储在 栈空间.堆空间.全局数据区: 2)所有对象共享类的成员函数:成员函数 只能存储在 代码段: 2 类中的静态成员(static) 类中的静态成员 1.用 static关键字 修饰: 2.可以用 类名::成员名 访问 静态成员: 3.静态成员 属于 整个类: 4.静态成员 是所属类的成员,其它类不能访问: 5.静态成员的内存分配

  • 详解Java中final的用法

    概念 final 具有"不可改变的"的含义,可以修饰 非抽象类.非抽象成员方法和变量. 用 final 修饰的类不能被继承,没有子类. 用 final 修饰的方法不能被子类的方法覆盖(重写). 用 final 修饰的变量表示常量,只能被赋一次值(声明变量的时候). 注: final 不能用来修饰构造方法,因为"方法覆盖"这一概念仅适用于类的成员方法,而不适用于类的构造方法,父类的构造方法和子类的构造方法之间不存在覆盖的关系,因此用final修饰构造方法没有任何意义.

随机推荐