【C++】static关键字及其修饰的静态成员变量/函数详解

🦄个人主页:修修修也

🎏所属专栏:C++

⚙️操作环境:Visual Studio 2022


目录

什么是static?

static的引入

静态数据的存储

全局(静态)存储区

static成员概念

static成员特性

ststic成员的应用

利用static实现一个可以计算程序中正在使用的类对象有多少的类

static成员妙解求1+2+3+...+n问题

结语


什么是static?

static的引入

static 是 C/C++ 中很常用的修饰符,它被用来控制变量的存储方式和可见性


静态数据的存储

全局(静态)存储区

        全局(静态)存储区内存区域中的划分,如下图所示:

        全局(静态)存储区:分为data段和bass段。data段(全局初始化区)存放初始化的全局变量和静态变量;bass段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中bass段在程序执行之前会被系统自动清0,所以未初始化的全局变量和静态变量在程序执行之前已经为0。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。

        在 C++ 中 static 的内部实现机制:静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。

        这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的 main() 函数前的全局数据声明和定义处。

        静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的"尺寸和规格",并不进行实际的内存分配,所以在类声明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。

        static 被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。

优势:

        可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。


static成员概念

        声明为static的类成员称为类的静态成员用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数静态成员变量一定要在类外进行初始化。


static成员特性

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。如下代码:
    class Student
    {
    public:
    	Student(const char name[], int idea, int grade)
    	{
    		strcpy(_name, name);
    		_idea = idea;
    		_grade = grade;
    	}
    	static int GetPostalCode()
    	{
    		return _postalCode;
    	}
    private:
    	char _name[10];
    	int _idea;
    	int _grade;
    
    	static int _postalCode;
    };
    int Student::_postalCode = 710400;
    
    int main()
    {
    	cout << Student::GetPostalCode() << endl;
    
    	Student s1("张三", 1001, 3);
    	Student s2("李四", 1002, 2);
    	Student s3("王五", 1003, 1);
    
    	return 0;
    }
    

    我们通过监控可以发现,在类里成员变量位置定义的静态成员变量并不存在于类对象中:也就是说,无论开辟了多少类对象,静态成员变量都只有一个,并且不属于任何类对象本身,只有成员变量才属于类对象。静态成员变量和类对象和其成员变量关系如下图:

  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针不能访问任何非静态成员;但非静态成员可以访问静态成员函数
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

ststic成员的应用

利用static实现一个可以计算程序中正在使用的类对象有多少的类

        我们可以利用对象创建必调用构造,而销毁必调用析构函数的特性,在类里创建一个static类对象来记录类对象的创建数/销毁数。注意,由于全局变量的不安全性,我们并不推荐使用全局变量来完成这项任务,如下代码,定义了一个可以计算程序中有多少类对象还在生命周期的类:

#include<iostream>
using namespace std;

class Count
{
public:
	//构造函数,每构造一个对象,scount+1
	Count() { ++_scount; }

	//const构造函数,每构造一个对象,scount+1
	Count(const Count& t) { ++_scount; }

	//析构函数,每析构一个对象,scount-1
	~Count() { --_scount; }

	//获取scount的值
	static int GetSCount() { return _scount; }

private:
	//变量_scount的声明
	static int _scount;
};
//变量_scount的定义
int Count::_scount = 0;

//创建全局Count对象a0
Count a0;

int main()
{
	cout << __LINE__ << ":" << Count::GetSCount() << endl;
	Count a1, a2;

	{
		Count a3(a1);
		cout << __LINE__ << ":" << Count::GetSCount() << endl;
	}//出了域作用限定范围a3的生命周期结束就自动析构了
	
	cout << __LINE__ << ":" << Count::GetSCount() << endl;

	return 0;
}

        我们测试一下这段代码是否可以统计当前行有多少个类对象正在使用:

        综上,对于类对象的创建数/销毁数的记录工作,可以从下面三个方向入手:

  • 类对象的创建数=构造函数静态成员变量++
  • 类对象的销毁数=析构函数静态成员变量++
  • 类对象的在生命周期数=构造函数静态成员变量-析构函数静态成员变量

设计一个类,在类外面只能在栈/只能在堆上创建对象

        如下,我们平常创建类对象的时候,如果不加以限制,则类对象可能被创建在不同的内存区域:

class A
{
public:
	A()
	{}
private:
	int _a1 = 1;
	int _a2 = 2;
};

int main()
{
	static A aa1;    //类对象在静态区
	A aa2;           //类对象在栈
	A* ptr = new A;  //类对象在堆

	return 0;
}

        但假如我们遇到了某种场景,即我们创建的这个类,只希望它在栈上创建对象/只希望它在堆上创建对象时,我们就可以通过将构造函数封装起来,再通过static修饰的类成员函数来创建指定的类对象,如:

class A
{
public:
	static A GetStackObj()
	{
		A aa;
		return aa;
	}
	static A* GetHeapObj()
	{
		return new A;
	}
private:
	A()//构造函数私有化
	{}
private:
	int _a1 = 1;
	int _a2 = 2;
};

int main()
{
	//static A aa1;    //类对象在静态区
	//A aa2;           //类对象在栈
	//A* ptr = new A;  //类对象在堆

	A::GetStackObj();
	A::GetHeapObj();

	return 0;
}

        这里有几点需要解释一下:

        1.将构造函数封装起来是为了不让类外的函数随便不按要求构造类对象,如:

        2.使用成员函数来创建类对象是因为成员函数调用类函数不受访问限定符的限制,如:

        3.使用static修饰成员函数是因为要解决无类对象就无法调用类成员函数的问题,如:

        做个梗图给大家形象理解一下这里的矛盾逻辑:

        综上,巧用封装和static就可以达到一些特殊的我们想实现的效果,要灵活使用啊。


static成员妙解求1+2+3+...+n问题

 一.题目描述

牛客网题目链接:JZ64 求1+2+3+...+nicon-default.png?t=N7T8https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId=13&tqId=11200&tPage=3&rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
描述:

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围: 0<n≤200
进阶: 空间复杂度 O(1) ,时间复杂度 O(n)

示例1:

输入:5

返回值:15

示例2:

输入:1

返回值:1

题目详情:


二.题目思路

  • 首先,我们创建一个Sum类,其中包含两个静态成员变量,一个是_i,一个是_ret
  • 其次,我们在主函数创建一个n个Sum类数据的数组,这意味着将要创建n个Sum类对象,则Sum的构造函数会被调用n次
  • 最后,我们在Sum的构造函数里让_ret+=_i后让_i++,这样,创建一个类对象_ret就会加等它的次序,即从1一直加到n。

三.解题代码

        根据上述思路,本题解题代码如下:

class Sum {
public:
    Sum()
    {
        _ret+=_i;
        _i++;
    }
    static int Getret()
    {
        return _ret;
    }
    private:
    static int _i;
    static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;

class Solution {
public:
    int Sum_Solution(int n) {
    Sum a[n];//创建了n个对象,调用了n次构造函数
    return Sum::Getret();
    }
};

        拷贝到牛客网测试运行:

        成功通过:


结语

希望这篇关于 static关键字及其修饰的静态成员变量/函数详解 的博客能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

相关文章推荐

【C++】构建第一个C++类:Date类

【C++】类的六大默认成员函数及其特性(万字详解)

【C++】内联函数

【C++】函数重载

【C++】什么是类与对象?

【C++】缺省参数(默认参数)

【C++】命名空间

【C++】“Hello World!“

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用 Docker Compose 安装 Harbor

Harbor 是一个企业级开源仓库&#xff0c;用于存储和管理 Docker 镜像。它提供了一系列功能&#xff0c;包括镜像复制、安全扫描和漏洞管理。Harbor 可以通过多种方式安装&#xff0c;其中之一是使用 Docker Compose。 先决条件 在安装 Harbor 之前&#xff0c;您需要满足以下…

golang sync.Map之如何设计一个并发安全的读写分离结构?

在 golang中&#xff0c;想要并发安全的操作map&#xff0c;可以使用sync.Map结构&#xff0c;sync.Map 是一个适合读多写少的数据结构&#xff0c;今天我们来看看它的设计思想&#xff0c;来看看为什么说它适合读多写少的场景。 如下&#xff0c;是golang 中sync.Map的数据结构…

详细分析Js中的Promise.all基本知识(附Demo)

目录 1. 基本知识2. Demo3. 实战 1. 基本知识 Promise.all 是 JavaScript 中的一个方法&#xff0c;它接受一个由 Promise 对象组成的数组作为参数&#xff0c;并在所有 Promise 对象都变为 resolved&#xff08;已完成&#xff09;状态时才返回一个新的 Promise 对象&#xf…

KTV点歌系统|基于JSP技术+ Mysql+Java+ B/S结构的KTV点歌系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

MyBatis是纸老虎吗?(四)

在《MyBatis是纸老虎吗&#xff1f;&#xff08;三&#xff09;》这篇文章中我们一起梳理了MyBatis配置文件的解析流程&#xff0c;并详细介绍了其中的一些常见节点的解析步骤。通过梳理&#xff0c;我们弄清楚了MyBatis配置文件中的一些常用配置项与Java Bean之间的对应关系&a…

linux网线正常,但没有网络,ifconfig没有ip地址

ubuntu 22.04环境&#xff1a; 今天正在用着好好的&#xff0c;不知道为什么突然没有网络了&#xff0c;网线灯也不亮&#xff0c;ifconfig只有lo回环地址。 因为装的双系统&#xff0c;切换到windows环境发现网络是正常的。 使用-a&#xff1a; 使用各种方式比如下面的命令…

大模型应用开发-虚拟人-AI刘能、AI李宏伟

简介 本案例通过python编程调用智谱的大模型接口,以及很简单的prompt设计,实现了用大语言模型模拟一个人物来和我们对话,前端HTML代码是用大语言模型生成的(原因:我根本不会写前端啊~~),本教程适合所有对大模型应用开发感兴趣的初学者,这是个非常有趣的案例。 读完本…

excel 破解 保护工作簿及保护工作表

excel 破解 保护工作簿及保护工作表 对于这种 保护工作簿及保护工作表 不知道密码时&#xff0c;可以使用以下方法破解 保护工作簿破解 打开受保存的excel 右键点击sheet名称 —> 查看代码 复制以下代码&#xff0c;粘贴到代码区域 Sub 工作簿密码破解() ActiveWorkbook.…

C语言例:(m=a==b)||(n=a==b);求解m,n的值

题目&#xff1a;设int a0,b0,m0,n0;执行语句(mab)||(nab);求解m,n的值。 #include<stdio.h> int main(void) {int a0,b0,m0,n0;(mab)||(nab);printf("m%d\n",m);printf("n%d\n",n);return 0; } 优先级: () 优先 优先 a b -->为真&am…

Python元组:不可变的序列

文章目录 一、元组1.创建元组2.访问元组中的元素3.修改元组4.删除元组 二、运算符1.加法运算符2.乘法运算符3.in运算符4.not in运算符 三、元组内置方法1.len()2.max()3.min()4.tuple()4.1 将列表转换成元组4.2 将字符串转换成元组4.3 将集合转换成元组 三、总结 一、元组 在P…

【5G NB-IoT NTN】3GPP R17 NB-IoT NTN介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

10000字!一文学会SQL数据分析

文章来源于山有木兮 原文链接&#xff1a;https://edu.cda.cn/goods/show/3412?targetId5695&preview0 第1节 SQL简介与基础知识 做数据分析的&#xff0c;为什么要写SQL&#xff1f; 没有数据的情况下&#xff0c;我们分析数据就像是巧妇难为无米之炊。因此&#xff0c…

Netty学习——源码篇3 服务端Bootstrap(一) 备份

1 介绍 在分析客户端的代码中&#xff0c;已经对Bootstrap启动Netty有了一个大致的认识&#xff0c;接下来在分析服务端时&#xff0c;就会相对简单。先看一下服务端简单的启动代码。 public class ChatServer {public void start(int port) throws Exception{NioEventLoopGro…

解锁鸿蒙小程序开发新姿势

如今&#xff0c;鸿蒙开发日益受到广大开发者的关注&#xff0c;而小程序开发也早已成为互联网领域的热门话题。那么&#xff0c;我们不禁要问&#xff1a;是否有可能将这两者融为一体&#xff0c;将小程序开发的便捷与高效带入鸿蒙生态中呢&#xff1f;本文将首先带你回顾小程…

SpringCloud alibaba入门简介

SpringCloud alibaba入门简介 1、简介 SpringCloud alibaba官网&#xff1a;SpringCloudAlibaba | Spring Cloud Alibaba (aliyun.com) Spring官网&#xff1a;Spring Cloud Alibaba GitHub中文文档&#xff1a;spring-cloud-alibaba/README-zh.md at 2022.x alibaba/spri…

数据库基本介绍及编译安装mysql

目录 数据库介绍 数据库类型 数据库管理系统&#xff08;DBMS&#xff09; 数据库系统 DBMS的工作模式 关系型数据库的优缺点 编译安装mysql 数据库介绍 数据&#xff1a;描述事物的的符号纪录称为数据&#xff08;Data&#xff09; 表&#xff1a;以行和列的形式组成…

公众号怎么更换主体

公众号账号迁移的作用是什么&#xff1f;只能变更主体吗&#xff1f;1.可合并多个公众号的粉丝、文章&#xff0c;打造超级大V2.可变更公众号主体&#xff0c;更改公众号名称&#xff0c;变更公众号类型——订阅号、服务号随意切换3.可以增加留言功能4.个人订阅号可迁移到企业名…

零知识玩转AVH(8)—— 门槛任务(3)所遇错误及解决(2)

接前一篇文章&#xff1a;零知识玩转AVH&#xff08;7&#xff09;—— 门槛任务&#xff08;2&#xff09;所遇错误及解决&#xff08;1&#xff09; 上一回说到在尝试完成门槛任务 https://github.com/ArmDeveloperEcosystem/Paddle-examples-for-AVH &#xff08;推荐&#…

阿里G6 树状图使用 Iconfont

官网&#xff1a;使用 Iconfont | G6 效果&#xff1a; 完整代码&#xff1a;index.html: <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width…

Python矩阵计算

文章目录 求积求逆最小二乘法特征值 Python科学计算&#xff1a;数组&#x1f4af;数据生成&#x1f4af;数据交互&#x1f4af;微积分&#x1f4af;插值&#x1f4af;拟合&#x1f4af;FFT&#x1f4af;卷积&#x1f4af;滤波&#x1f4af;统计 求积 矩阵是线性代数的核心对…
最新文章