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

news2025/1/16 5:44:30

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

一  static 有以下几种用法:

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

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

3. 类的静态成员变量

4. 静态类对象

5. 类的静态方法

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

// main.cpp
#include<iostream>

int  xxx = 66; 

static int yyy = 888;

int main()
{
   std::cout << xxx << std::endl;
   std::cout << xxx << std::endl;
   
   return ;
}
  • 全局变量特点:

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

  • 静态全局变量特点:

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

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

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

// c.h
#include<iostream>
#include<string>

class C
{
public:
    C(std::string n):name(n)
    {
        std::cout << "constructor C " << std::endl;
    }
    ~C(){
        std::cout << "destructor C " << std::endl;
    }

    std::string  getName()
    {
        return this->name;
    }

private:
    std::string name;
};


// main.cpp

#include<iostream>
#include"c.h"

void  testStatic()
{
   static  int  count = 0;  // 只会被初始化一次
   cout << "count: " << count++ << endl; // 每次调用该函数都会将上次存储的值打印出来
}

C& getTestStaticC()
{
    static C c("I'm static object."); // 只初始化一次,生命周期与程序等长

    return c;
}


int main()
{

    testStatic();
    testStatic();
    testStatic();

    C& cc1 = getTestStaticC(); // 因为定义的静态变量存储在静态存储区,不是在栈上,因此离开函数作用域以后,对静态的引用仍然是可以访问的
    C& cc2 = getTestStaticC(); // 因为定义的静态变量存储在静态存储区,不是在栈上,因此离开函数作用域以后,对静态的引用仍然是可以访问的

    std::cout << "cc1: " << cc1.getName() << std::endl;
    std::cout << "cc2: " << cc2.getName() << std::endl;
    C  cc3("666888");

    return 0;
}


输出:

3. 类的静态成员变量

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

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

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

// c.h
#include<iostream>
#include<string>

class C
{
public:
    C(std::string n):name(n)
    {
        std::cout << "constructor C name: " << this->name << std::endl;
    }
    ~C(){
        std::cout << "destructor C name: " << this->name << std::endl;
    }

    std::string  getName()
    {
        return this->name;
    }


private:
public:
    std::string name;
    static int height;
};


// c.cpp
#include "c.h"

int C::height = 66; // 静态成员变量需要显示的定义在类的外部


// main.cpp
#include"c.h"
#include<iostream>

int& testStatic21()
{
    C c("static21 test");
    std::cout << "name: " << c.getName() << " , height: " << c.height << std::endl;

    return c.height;
}
std::string& testStatic22()
{
    C c("static22 test");
    std::cout << "name: " << c.getName() << " , height: " << c.height << std::endl;

    return c.name;
}

int main()
{
    int& abc = testStatic21();

    std::cout << "abc: " << abc << std::endl; // 因为存储在静态存储区,所以离开函数作用域后,变量仍然存在

    std::string& name = testStatic22();
    std::cout << "name: " << name << std::endl; // 非静态成员变量实际存储位置是函数的栈空间中,因此离开函数作用域后,就会被释放掉,所以获取结果未可知

  return 0;
}

输出:

4. 静态类对象

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

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

// c.h
#include<iostream>
#include<string>

class C
{
public:
    C(std::string n):name(n)
    {
        std::cout << "constructor C name: " << this->name << std::endl;
    }
    ~C(){
        std::cout << "destructor C name: " << this->name << std::endl;
    }

    std::string  getName()
    {
        return this->name;
    }


private:
public:
    std::string name;
};


// main.cpp
int main()
{
   C& cc = testStatic3();

   std::cout << "cc: " << cc.getName() << std::endl; // 静态对象存储在静态存储区, 所以离开函数作用域后,仍然存在

  return 0.
}

输出:

5. 类的静态方法

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

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

// c.h
#include<iostream>
#include<string>

class C
{
public:
    C(std::string n):name(n)
    {
        std::cout << "constructor C name: " << this->name << std::endl;
    }
    ~C(){
        std::cout << "destructor C name: " << this->name << std::endl;
    }

    std::string  getName()
    {
        return this->name;
    }

    static void print()
    {
        std::cout << "print height: " << height << std::endl;
    }



private:
public:
    std::string name;

    static int height;
};

// c.cpp
#include "c.h"


int C::height = 66;


// main.cpp
#include"c.h"
#include<iostream>

void testStatic4()
{
    C::print();
    C c("888");
    c.print();
}

int main()
{
  testStatic4();

  return 0;
}

二    static 变量几种初始化方式

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

三种初始化:

1. 编译时初始化

2. 程序加载时初始化

3. 运行时初始化

1. 编译时初始化

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

/ main.cpp

static int xx = 666;

int main()
{

   return 0;
}

2. 程序加载时初始化

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

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

// main.cpp

int x = 6;
int y = 8;
static int z = x + y;

int main()
{

  return 0;
}

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

// d.h
#include<iostream>
#include<string>

class D
{
public:
    D(std::string n):name(n)
    {
        std::cout << "constructor D name: " << this->name << std::endl;
    }
    ~D()
    {
        std::cout << "destructor D name: " << this->name << std::endl;
    }

private:
    std::string name;
};

// c.h
class C
{
public:
    C(std::string n):name(n)
    {
        std::cout << "constructor C name: " << this->name << std::endl;
    }
    ~C(){
        std::cout << "destructor C name: " << this->name << std::endl;
    }

    std::string  getName()
    {
        return this->name;
    }

    static void print()
    {
        std::cout << "print height: " << height << std::endl;
    }



private:
public:
    std::string name;

    static int height;
    static D d;
};

// c.cpp
int C::height = 66;

D C::d = D("CD static");

// main.cpp
#include<string>
#include<iostream>
#include"c.h"

void testStatic5()
{
    C c("testStatic5 -- ");
    std::cout << "testStatic5 c name: " << c.getName() << std::endl;
}

static D d("before main inialiaze!");

int main()
{

  std::cout << "enter int main func!!" << std::endl;
  testStatic5();

  return 0;
}

3. 运行时初始化

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

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

// d.h
class D
{
public:
    D(std::string n):name(n)
    {
        std::cout << "constructor D name: " << this->name << std::endl;
    }
    ~D()
    {
        std::cout << "destructor D name: " << this->name << std::endl;
    }

public:
    std::string name;
};


// main.cpp

#include<iostream>
#include"d.h"


void testStaticInitialize(){
    static D d("testStaticInitialize --- ");
}

int main()
{
  std::cout << "enter int main func!!" << std::endl;
  static D dd("dd --- ");
  testStaticInitialize();

  return 0;
}

输出:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/998332.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

管理类联考——数学——汇总篇——知识点突破——应用题——交叉比例法/杠杆原理

读书笔记 甲有&#xff1a;x个a&#xff0c;乙有&#xff1a;y个b&#xff0c;甲乙的平均值为c&#xff0c;根据总数相等&#xff0c;得&#xff1a;axbyc(xy)&#xff0c;即ax-cxcy-by&#xff0c;则 x y c − b a − c \frac{x}{y}\frac{c-b}{a-c} yx​a−cc−b​ &#…

【Vue2.0源码学习】生命周期篇-初始化阶段(initState)

文章目录 1. 前言2. initState函数分析3. 初始化props3.1 规范化数据3.2 initProps函数分析3.3 validateProp函数分析3.4 getPropDefaultValue函数分析3.5 assertProp函数分析 4. 初始化methods5. 初始化data6. 初始化computed6.1 回顾用法6.2 initComputed函数分析6.3 defineC…

rv1126之isp黑电平(BLC)校准!

前言&#xff1a; 大家好&#xff0c;今天我们继续来讲解isp第二期内容&#xff0c;这期内容主要分三个部分&#xff1a; 1、tunning的工作流程 2、利用RKISP2.x_Tuner来创建tunning工程&#xff0c;并连接上rv1126开发板进行抓图 3、BLC(黑电平校准)的原理和校准方法以及实战…

UE4(Unreal Engine 4)运行setup.bat发生403报错的问题

最近UE官方在迁移服务器&#xff0c;有些D:\UE4\Engine\Build\Commit.gitdeps.xml文件需要更新。此时需要你去往UE对应的版本下载新的Commit.gitdeps.xml文件&#xff0c;并且覆盖原有的Commit.gitdeps.xml文件。UE的官方说明 覆盖前 覆盖后

Tomcat多实例与负载均衡

Tomcat多实例与负载均衡 一、Tomcat多实例1.1、安装JDK1.2、安装tomcat1.3、配置tomcat环境变量1.4、修改tomcat中的主配置文件1.5、修改启动脚本和关闭脚本1.6、 启动tomcat并查看 二、NginxTomcat负载均衡、动静分离2.1、部署Nginx负载均衡2.2、部署第一台tomcat2.3、部署第二…

Windows系统远程桌面连接CentOS7

1. 安装 GNOME 桌面环境&#xff08;如果尚未安装&#xff09; yum groupinstall "GNOME Desktop" 2. 安装 VNC Server yum install tigervnc-server 设置 vnc 账号密码 vncpasswd root root 是账号&#xff0c;接下会提示两次输入密码 3. 安装 xrdp 检查cento…

编译器01-整体概述

一&#xff1a;编译器各个阶段及它们之间接口 二&#xff1a;编译器各个阶段解释 三&#xff1a;编译器中常用的数据结构-树与链表

基于springboot+vue的实验室耗材管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

Mysql数据库之常用SQL语句及事务学习总结

数据库介绍 几个常见的缩写&#xff1a; DB&#xff1a;数据库。全称&#xff1a;DataBase。DBMS&#xff1a;数据库管理系统。全称&#xff1a;DataBase Management System。DBS&#xff1a;数据库系统。全称&#xff1a;DataBase System。DBA&#xff1a;数据库管理员。全称…

Matlab图像处理-分水岭算法

基本概念 最常用的分水岭算法是F.Meyer在20世纪90年代早期提出的基于灰度图像的分割算法&#xff0c;它是一种基于拓扑理论的数学形态学的分割方法&#xff0c;其基本思想是把图像看作是测地学上的拓扑地貌&#xff0c;图像中每一点像素的灰度值表示该点的海拔高度&#xff0c…

二分搜索树层序遍历(Java 实例代码)

目录 二分搜索树层序遍历 Java 实例代码 src/runoob/binary/LevelTraverse.java 文件代码&#xff1a; 二分搜索树层序遍历 二分搜索树的层序遍历&#xff0c;即逐层进行遍历&#xff0c;即将每层的节点存在队列当中&#xff0c;然后进行出队&#xff08;取出节点&#xff0…

分身空间(应用多开)会员版,提供更便捷的应用多开体验

分身空间&#xff08;应用多开&#xff09;会员版是一款功能强大的跨平台多设备同步工具。通过这个应用&#xff0c;您可以在手机、平板电脑和电脑等多个设备上同时登录和使用多个账号&#xff0c;实现应用的多开。 无论是社交媒体、游戏、聊天工具还是其他应用&#xff0c;分…

Windows MySQL服务安装及问题解决方案

Windows MySQL服务安装及问题解决方案 安装及配置步骤一&#xff1a;官网下网MySQL安装包步骤二&#xff1a;设置环境变量步骤仨&#xff1a;配置MySQL,ini配置文件步骤四&#xff1a;初始化MySQL步骤五&#xff1a;开启MySQL服务步骤六&#xff1a;测试是否安装成功步骤七&…

【Linux】Systemd 中的单元(Unit)和单元文件(Unit File)怎么理解?

单元&#xff08;Unit&#xff09;单元文件&#xff08;Unit File&#xff09;感谢 &#x1f496; 关于systemd是什么&#xff0c;http://t.csdn.cn/pMkG7这篇文章里有详细说明。 这篇文件我们一起来看看Systemd 中的单元&#xff08;Unit&#xff09;和单元文件&#xff08;Un…

C语言经典100例题(50)--#include 的应用练习

目录 题目 问题分析 代码 运行结果 题目 #include 的应用练习 问题分析 头文件的定义&#xff1a;#include 指令告诉预处理器打开指定的文件&#xff0c;并且把此文件的内容插入到当前文件中。因此&#xff0c;如果想让几个源文件可以访问相同的信息&#xff0c;可以把…

算法通关村18关 | 透析回溯的模板

回溯有清晰的解题模板&#xff0c; void backtracking(参数){if (终止条件){存放结果;return;}for (选择本层中的集合元素&#xff08;画成树&#xff0c;就是树节点孩子的大小) {处理节点;backtracking();回溯&#xff0c;撤销处理结果;}} 1. 从N叉树说起 在回溯之前&#x…

克隆阿里巴巴镜像网站的yum源制作自己的yum源

“你会在一间炉火昏黄的房子里死去&#xff0c;而你父亲的魔法全然失效。那张天鹅绒床是如此温热&#xff0c;你的死亡尚有余温之时&#xff0c;我将从风雪中归来……” 主要使用到 nginx 服务器制作 与 vsftpd 服务器不一样的是 nginx 使用到的是 http 协议 xsftpd 使用到的是…

第14章_瑞萨MCU零基础入门系列教程之QSPI

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

103. 二叉树的锯齿形层序遍历

103. 二叉树的锯齿形层序遍历 题目-中等难度示例1. bfs 题目-中等难度 给你二叉树的根节点 root &#xff0c;返回其节点值的 锯齿形层序遍历 。&#xff08;即先从左往右&#xff0c;再从右往左进行下一层遍历&#xff0c;以此类推&#xff0c;层与层之间交替进行&#xff09…

常用串口调试工具(列表)

目录 串口调试助手(微软商店可以下载&#xff0c;无广告)UartAssist(串口调试助手)串口调试助手 5.13.1 串口调试助手(微软商店可以下载&#xff0c;无广告) 链接: https://apps.microsoft.com/store/detail/%E4%B8%B2%E5%8F%A3%E8%B0%83%E8%AF%95%E5%8A%A9%E6%89%8B/9NBLGGH4…