C/C++的static有什么用

前言

既然要学习记录的碎片那么多,不如一个碎片一篇文章。

本文全介绍C/C++的static关键字,并记录自己的问题。

C和C++里面static除了可以修饰C++比C多出来的类什么的,还有什么不同?

我们主要针对C++介绍。

什么是C++中的static关键字

static意为静态,static关键字的作用很多:

比如在类中修饰变量/函数,变为静态成员变量/函数;修饰全局变量/函数,变为静态全局变量/函数;修饰局部变量,变为静态局部变量……

  • 在类中修饰变量/函数

    类中普通的非static成员,每个实例化的类都有自己的一份拷贝,在程序的栈区或堆区分配内存;

    而使用static修饰后,类中的静态成员变量,在程序的全局数据区分配内存(所以不初始化的话默认是全0),由所有实例化的类共享,而不会为每个实例化的类都分配一份,不会被计算在类的sizeof()内;而且static成员变量在任何类实例存在之前就会被定义和初始化(需要在类内声明,类和函数之外定义和初始化),也就是在创建任何类实例之前就可以操作它。

在这里插入图片描述

那不会出现不同实例化类访问冲突的问题吗?

static修饰成员函数也是同理,静态成员函数与任何实例化的类无关,可以在类实例化之前调用。但需要注意的是静态成员函数没有this 指针,只能访问静态成员

最典型的例子就是几乎所有static的介绍都会提到的:统计类实例化的数量。

#include <iostream>
using namespace std;

class myclass {
public:
    int a;
    static int cnt; // 注意 static 数据成员需要在类内声明, 在类外定义和初始化

    myclass() : a(0) { cnt++; } // 构造函数中自增

    ~myclass() { cnt--; } // 析构函数中自减

    static void getCnt() {
        cout << "myclass instance cnt is: "<<myclass::cnt << endl; // 这里写成cnt也可以正常运行
    }
};

// 需要注意的是如果在类外定义静态成员函数getCnt(),不能加static(因为已在类中声明),应该:
// void myclass::getCnt() {...}

int myclass::cnt = 0; // static 数据成员必须在类和函数之外定义和初始化

int main() {
    // 静态成员函数与具体的类实例无关,可以直接通过类名::函数名调用
    myclass::getCnt(); // 0
    myclass m1{};
    auto pm2 = new myclass();
    myclass::getCnt(); // 2
    delete pm2;
    myclass::getCnt(); // 1
    return 0;
}

程序显示:

myclass instance cnt is: 0
myclass instance cnt is: 2
myclass instance cnt is: 1

这里涉及**声明(Declaration)定义(Definition)**的区别,简单讲就是:

声明是指出存储类型,并给存储单元指定名称(就是变量名称)。

定义是分配内存空间,还可以包括为变量指定初值

  • 在类中修饰成员函数

    与上一部分类中的静态成员变量类似,

  • 修饰全局变量

    也就指向这个问题:普通的全局变量和加了static的静态全局变量,有什么区别?

    这个问题涉及变量的作用域

    作用域:变量的可见代码域,即变量的有效范围,可以在哪部分代码内使用。

    全局变量和静态全局变量都在进程的全局数据区分配内存,声明周期都是程序的整个运行期,不同的是他们的作用域

    对于非静态的全局变量来说,默认作用域是整个程序,也就是所有的代码文件,包括源文件和头文件。比如我们定义一个全局变量glob,在另一个源文件test.cpp中同样可以使用:

    /
    /// main.cpp
    #include <iostream>
    #include "test.cpp"
    
    using namespace std;
    
    int glob = 555; // 非静态全局变量,作用域在所有源文件
    
    int main() {
        cout << "glob in main.cpp: " << glob << endl;
        PrintcntS();
        return 0;
    }
    
    /
    /// test.cpp
    #include <iostream>
    
    using namespace std;
    
    extern int glob; // extern用来说明glob变量是在别处定义的,要在此处引用
    
    void PrintcntS() {
        cout << "glob in test.cpp: " << glob << endl;
    }
    

    程序会正常运行,输出:

    glob in main.cpp: 555
    glob in test.cpp: 555
    

    但如果将main.cpp中的全局变量改为:

    static int glob = 555;  
    

    那么作用域会变为当前源文件,本例也就是只在main.cpp中有效,test.cpp将无法通过extern使用glob变量,编译器会报错。

  • 修饰全局函数

    也就是变为静态全局函数,与非静态全局函数的区别和全局变量/静态全局变量同理,也是作用域不同,全局函数作用域在所有程序文件,静态全局函数作用域在该文件内。我们也以一个类似的例子说明:

    /
    /// main.cpp
    #include <iostream>
    #include "test.cpp"
    
    using namespace std;
    
    void globFunc() {
        cout << "I am globFunc." << endl;
    }
    
    int main() {
        globFunc();
        testFunc();
        return 0;
    }
    
    /
    /// test.cpp
    #include <iostream>
    
    using namespace std;
    
    extern void globFunc();
    
    void testFunc() {
        globFunc();
    }
    

    当把main.cpp中的globFunc改为静态全局函数后,编译器报错;

    static void globFunc() {
        cout << "I am globFunc." << endl;
    }
    
  • 修饰局部变量

    这节涉及变量的生命周期

    生命周期:变量的生命周期指的是在程序运行期间变量有效存在的时间间隔。即存在于内存的时间。

    正常来讲,局部变量在栈或堆区分配内存,生命周期在当前的局部范围的开始到结束;拿下面例子的func2()i讲,其生命周期就在函数内,函数执行完了i就被从内存中删掉了。

    而静态局部变量,会在全局数据区分配内存,生命周期是整个程序的开始到结束,但是作用域也是仅在当前局部范围内,比如下面例子的func1()中的i,在全局区分配内存,函数运行结束后会以运行完后的值一直存在全局区,下次运行func1()时会自动跳过初始化赋值,以之前的值继续参与;

    #include <iostream>
    using namespace std;
    
    void func1() {
        static int i = 10; // 静态局部变量的初始化赋值只在首次声明时生效,以后会自动跳过
        cout << "static local i is: "<< i << endl;
        i += 10;
    }
    
    void func2() {
        int i = 10;
        cout << "non-static local i is: "<< i << endl;
        i += 10;
    }
    
    int main() {
        func1(); // 10
        func1(); // 20
    
        func2(); // 10
        func2(); // 10
        return 0;
    }
    

    程序输出:

    static local i is: 10
    static local i is: 20
    non-static local i is: 10
    non-static local i is: 10
    

最后

仅作为自己的笔记,有错误或补充欢迎指出!

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WuPeng_uin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值