• C++ -- 学习系列 static 关键字的使用


    static 是 C++ 中常用的关键字,被 static 修饰的变量只会在 静态存储区(常量数据也存放在这里) 被分配一次内存,生命周期与整个程序一样,随着程序的消亡而消亡。

    一  static 有以下几种用法:

    1. 在文件中定义的 静态全局变量

    2. 在函数中定义的静态变量

    3. 类的静态成员变量

    4. 静态类对象

    5. 类的静态方法

    1. 在文件中定义的 静态全局变量

    1. // main.cpp
    2. #include
    3. int xxx = 66;
    4. static int yyy = 888;
    5. int main()
    6. {
    7. std::cout << xxx << std::endl;
    8. std::cout << xxx << std::endl;
    9. return ;
    10. }
    • 全局变量特点:

    全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过 extern 全局变量名的声明,就可以使用全局变量。

    • 静态全局变量特点:

    全局静态变量是显式用 static 修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用 extern 声明也不能使用。

    2. 在函数中定义的静态局部变量

    在函数中定义的静态变量仅仅初始化一次,且变量会存储到静态存储区,因此即便离开函数作用域也不会消失。

    1. // c.h
    2. #include
    3. #include
    4. class C
    5. {
    6. public:
    7. C(std::string n):name(n)
    8. {
    9. std::cout << "constructor C " << std::endl;
    10. }
    11. ~C(){
    12. std::cout << "destructor C " << std::endl;
    13. }
    14. std::string getName()
    15. {
    16. return this->name;
    17. }
    18. private:
    19. std::string name;
    20. };
    21. // main.cpp
    22. #include
    23. #include"c.h"
    24. void testStatic()
    25. {
    26. static int count = 0; // 只会被初始化一次
    27. cout << "count: " << count++ << endl; // 每次调用该函数都会将上次存储的值打印出来
    28. }
    29. C& getTestStaticC()
    30. {
    31. static C c("I'm static object."); // 只初始化一次,生命周期与程序等长
    32. return c;
    33. }
    34. int main()
    35. {
    36. testStatic();
    37. testStatic();
    38. testStatic();
    39. C& cc1 = getTestStaticC(); // 因为定义的静态变量存储在静态存储区,不是在栈上,因此离开函数作用域以后,对静态的引用仍然是可以访问的
    40. C& cc2 = getTestStaticC(); // 因为定义的静态变量存储在静态存储区,不是在栈上,因此离开函数作用域以后,对静态的引用仍然是可以访问的
    41. std::cout << "cc1: " << cc1.getName() << std::endl;
    42. std::cout << "cc2: " << cc2.getName() << std::endl;
    43. C cc3("666888");
    44. return 0;
    45. }

    输出:

    3. 类的静态成员变量

    类的静态成员变量属于类的所有对象,其存储在静态存储区,只有一个存储空间;而其他的非静态变量属于每个对象,在每个对象中都有其副本。

    静态成员变量不在构造函数中初始化,因此静态成员变量不依赖于对象。

    静态成员变量必须显示的初始化,一般情况下,我们都在类的外部对静态成员变量初始化,若是静态成员变量未被初始化,编译链接时,会报错。

    1. // c.h
    2. #include
    3. #include
    4. class C
    5. {
    6. public:
    7. C(std::string n):name(n)
    8. {
    9. std::cout << "constructor C name: " << this->name << std::endl;
    10. }
    11. ~C(){
    12. std::cout << "destructor C name: " << this->name << std::endl;
    13. }
    14. std::string getName()
    15. {
    16. return this->name;
    17. }
    18. private:
    19. public:
    20. std::string name;
    21. static int height;
    22. };
    23. // c.cpp
    24. #include "c.h"
    25. int C::height = 66; // 静态成员变量需要显示的定义在类的外部
    26. // main.cpp
    27. #include"c.h"
    28. #include
    29. int& testStatic21()
    30. {
    31. C c("static21 test");
    32. std::cout << "name: " << c.getName() << " , height: " << c.height << std::endl;
    33. return c.height;
    34. }
    35. std::string& testStatic22()
    36. {
    37. C c("static22 test");
    38. std::cout << "name: " << c.getName() << " , height: " << c.height << std::endl;
    39. return c.name;
    40. }
    41. int main()
    42. {
    43. int& abc = testStatic21();
    44. std::cout << "abc: " << abc << std::endl; // 因为存储在静态存储区,所以离开函数作用域后,变量仍然存在
    45. std::string& name = testStatic22();
    46. std::cout << "name: " << name << std::endl; // 非静态成员变量实际存储位置是函数的栈空间中,因此离开函数作用域后,就会被释放掉,所以获取结果未可知
    47. return 0;
    48. }

    输出:

    4. 静态类对象

    static 关键字对类对象的工作方式也相同。声明为 static 的对象将分配到静态存储区中,并且一直作用到程序结束。

    注:使用 static 关键字分配为零仅适用于原始数据类型,不适用于用户定义的数据类型。

    1. // c.h
    2. #include
    3. #include
    4. class C
    5. {
    6. public:
    7. C(std::string n):name(n)
    8. {
    9. std::cout << "constructor C name: " << this->name << std::endl;
    10. }
    11. ~C(){
    12. std::cout << "destructor C name: " << this->name << std::endl;
    13. }
    14. std::string getName()
    15. {
    16. return this->name;
    17. }
    18. private:
    19. public:
    20. std::string name;
    21. };
    22. // main.cpp
    23. int main()
    24. {
    25. C& cc = testStatic3();
    26. std::cout << "cc: " << cc.getName() << std::endl; // 静态对象存储在静态存储区, 所以离开函数作用域后,仍然存在
    27. return 0.
    28. }

    输出:

    5. 类的静态方法

    与类的静态成员变量类似,类的静态方法也属于类,任何类的对象都可以调用此类方法。

    既可以通过 对象名. 静态方法 调用,也可以通过类名::静态方法,后一种方法更常用。

    1. // c.h
    2. #include
    3. #include
    4. class C
    5. {
    6. public:
    7. C(std::string n):name(n)
    8. {
    9. std::cout << "constructor C name: " << this->name << std::endl;
    10. }
    11. ~C(){
    12. std::cout << "destructor C name: " << this->name << std::endl;
    13. }
    14. std::string getName()
    15. {
    16. return this->name;
    17. }
    18. static void print()
    19. {
    20. std::cout << "print height: " << height << std::endl;
    21. }
    22. private:
    23. public:
    24. std::string name;
    25. static int height;
    26. };
    27. // c.cpp
    28. #include "c.h"
    29. int C::height = 66;
    30. // main.cpp
    31. #include"c.h"
    32. #include
    33. void testStatic4()
    34. {
    35. C::print();
    36. C c("888");
    37. c.print();
    38. }
    39. int main()
    40. {
    41. testStatic4();
    42. return 0;
    43. }

    二    static 变量几种初始化方式

    C++中static变量的初始化_c++ static 重新初始化_LikeMarch的博客-CSDN博客

    三种初始化:

    1. 编译时初始化

    2. 程序加载时初始化

    3. 运行时初始化

    1. 编译时初始化

    若 静态全局变量 基本数据类型(POD) ,且初始化值为常量,那么该变量会在编译期初始化。

    1. / main.cpp
    2. static int xx = 666;
    3. int main()
    4. {
    5. return 0;
    6. }

    2. 程序加载时初始化

    程序被加载时立即初始化,该过程发生在main 函数执行前。即使程序任何地方都没访问过该变量,仍然会进行初始化,因此形象地称之为"饿汉式初始化"。

    2.1  静态全局变量初始化(初始值不是常量时),此时变量初始化是在程序加载时初始化的。

    1. // main.cpp
    2. int x = 6;
    3. int y = 8;
    4. static int z = x + y;
    5. int main()
    6. {
    7. return 0;
    8. }

    2.2  静态全局变量不是基本类型,此时变量初始化是在程序加载时初始化的。

    1. // d.h
    2. #include
    3. #include
    4. class D
    5. {
    6. public:
    7. D(std::string n):name(n)
    8. {
    9. std::cout << "constructor D name: " << this->name << std::endl;
    10. }
    11. ~D()
    12. {
    13. std::cout << "destructor D name: " << this->name << std::endl;
    14. }
    15. private:
    16. std::string name;
    17. };
    18. // c.h
    19. class C
    20. {
    21. public:
    22. C(std::string n):name(n)
    23. {
    24. std::cout << "constructor C name: " << this->name << std::endl;
    25. }
    26. ~C(){
    27. std::cout << "destructor C name: " << this->name << std::endl;
    28. }
    29. std::string getName()
    30. {
    31. return this->name;
    32. }
    33. static void print()
    34. {
    35. std::cout << "print height: " << height << std::endl;
    36. }
    37. private:
    38. public:
    39. std::string name;
    40. static int height;
    41. static D d;
    42. };
    43. // c.cpp
    44. int C::height = 66;
    45. D C::d = D("CD static");
    46. // main.cpp
    47. #include
    48. #include
    49. #include"c.h"
    50. void testStatic5()
    51. {
    52. C c("testStatic5 -- ");
    53. std::cout << "testStatic5 c name: " << c.getName() << std::endl;
    54. }
    55. static D d("before main inialiaze!");
    56. int main()
    57. {
    58. std::cout << "enter int main func!!" << std::endl;
    59. testStatic5();
    60. return 0;
    61. }

    3. 运行时初始化

    程序执行到静态变量的定义引用时,才会初始化,因此也被称为“懒汉式初始化”。

    比如静态局部变量就是典型的 运行时初始化。

    1. // d.h
    2. class D
    3. {
    4. public:
    5. D(std::string n):name(n)
    6. {
    7. std::cout << "constructor D name: " << this->name << std::endl;
    8. }
    9. ~D()
    10. {
    11. std::cout << "destructor D name: " << this->name << std::endl;
    12. }
    13. public:
    14. std::string name;
    15. };
    16. // main.cpp
    17. #include
    18. #include"d.h"
    19. void testStaticInitialize(){
    20. static D d("testStaticInitialize --- ");
    21. }
    22. int main()
    23. {
    24. std::cout << "enter int main func!!" << std::endl;
    25. static D dd("dd --- ");
    26. testStaticInitialize();
    27. return 0;
    28. }

    输出:

  • 相关阅读:
    一阶滞后低通滤波器(支持采样频率设置 博途SCL代码)
    openlayers图层数据覆盖
    使用Qt进行HTTP通信的方法
    面试突击:说一下 Spring 中 Bean 的生命周期?
    【Spring Cloud】Nacos 配置管理详解
    C语言经典面试题目(十六)
    Metabase学习教程:仪表盘-4
    【算法1-4】递推与递归-P1002 [NOIP2002 普及组] 过河卒
    深入理解面向对象(第二篇)
    【动手学深度学习】权重衰减(含Pytorch从零开始实现的源代码)
  • 原文地址:https://blog.csdn.net/qq_33775774/article/details/132789878