const,static深度总结——c++穿透式分析

         前言c++类和对象的知识点中除了几种默认函数, 比较重要的还有使用const和static修饰成员相关知识点。const在c++中特性很简单。 但是在使用中, 比较容易疏忽大意出现问题。 static特性也很简单, 但是比起const来要直接的多。 在使用中只要熟练语法以及底层原理。就不容易出错。

         本节内容主要是对其进行详细的解析。相信本节内容可以让你对这两种语法有一个更深层次的认识。(注:如果想要进阶const的使用 以及 对类和对象的认识, 建议自己实现vector的stl。 list也可以, 但是list的stl的迭代器比较复杂, 需要对类和对象有一定的理解。)

const

在讨论const修饰类的成员之前。 我们先需要知道const的用法。

c++的const 与c语言const的区别

        c++中的const比c语言中的const更加严格。在c语言中, 我们使用const定义一个常量后, 这个常量并不是真正意义上的常量,它还保存在栈区。  我们如果定义一个指向这个空间的指针。 那么我们还是可以对这块空间进行修改的

        但是在c++中。 如果我们使用const定义了一个常量。 那么我们如果再使用一个指针变量保存这块内存的地址。 会报错。编译不通过。 这是因为使用const修饰的变量相当于放在了常量区(这里是相当于放在了常量区。事实上,当我们进行源代码编译的时候, 变量被编译成对应的常量, 这个常量保存在哪个区, 取决于你在哪里定义的变量。 如果是在栈区, 那就是被存储在了栈区。 所以使用const修饰变量不会改变该数据存储的位置。这个位置是在编译时期就决定的, 不是在运行时期决定的。)我们想要访问这块空间只能通过常量指针来进行访问(只读)。

const的权限平移,放大, 缩小问题

        在c++中, 权限只能平移或者缩小, 不能放大。  如图:

int main()
{
    const int a = 10;
    int& b = a;//不可以, const修饰a,让a成为一个常量,数据放在了常量区,只读。 int& b是一个可读可写的类型。 相当于权限放大。  
    int* Pa = &a;//不可以, a是一个常量,数据放在了常量区。a的内容不可被修改, 只读。 使用指针找到这块空间后,也不能修改。 但是pa是一个可读可写的类型。 相当于权限的放大。
    const int* pa = &a;//可以, 这里pa使用const修饰, 变成只读。 相当于权限的平移。 
  
    int c = 10;
//下面这两个都可以, c是变量, 可读可写。而d别名是只读的。 pc也是只读的。都相当于权限的缩小。 
    const int& d = c;
    const int* pc = &c;
 
    return 0
}

        看这张图中的红框框, 红框框是使用const修饰的一个int类型的变量。 然后使用了一个int类型的引用b作为a的别名。

         这么写是错误的。 a被const修饰, 具有常性,只读。 b是一个不加const修饰的引用别名, 只是一个单纯的变量, 可读可写 想一想,如果这里是对的, a本身都不可以改变自己, 那么如果通过b这个别名可以来对a进行修改, 这不就乱了吗?所以, c++设定了权限只能平移和缩小, 不能放大。

同样的, 我们看一下下面的几个示例。

      1.首先, 我们来看一下红框框和绿框框, 这个对不对呢?

答案是对的, 因为a被const修饰, 具有常性。 但是pa也被const在int*前面修饰, 这里相当于指针指向的空间不可被修改, 称为常量指针(常量指针指向常量的指针, 另一个叫做指针常量, 是不能改变指向的一个指针常量)。所以是权限的平移, 可以被编译器通过。

        2.然后, 我们来看一看蓝框框和黄框框是不是对的呢?

答案是对的, c是一个int类型的变量。 而d作为c的别名, pc作为c的指针。 都是被const修饰, 都具有常性。 权限缩小。 编译器可以通过。

const 修饰类中的成员

        在C++中,const关键字可以用来修饰类的成员函数。一个被const修饰的成员函数被称为常量成员函数,它不会修改类的内部状态。

   常量成员函数的声明和定义格式如下:

        常量成员函数可以在常对象上调用,它们也可以在非常对象上调用。但是,常量对象上只能调用常量成员函数,而非常对象上可以调用常量成员函数和非常量成员函数。因为常对象是一个只读的类型。而且类的实例化对象在调用成员函数的时候会把自己的this指针传送过去。 而常量成员函数中的const本质上是修饰的成员this指针。所以常对象在调用常量成员函数的时候, 地址给const修饰的this,是权限的平移。 可以编译通过。 但是如果常对象调用非常量成员函数, this指针是可读可写的, 属于权限的放大。编译不能通过。

        const修饰成员函数, 本质上就是修饰的成员函数中的this指针。 让this指针从可读可写变为只读的,也就是常量指针(注意区别常量指针和指针常量的区别)。 

const修饰返回值和修饰成员函数

        const修饰返回值和const修饰成员函数是不同的。 但是我们通过是否使用const修饰成员函数来区分一个成员函数的返回值是否是const修饰的常量。 就比如迭代器的实现, stl中的迭代器一般都有如下两种类型:

​
iterator;//迭代器
const_iterator;//只读迭代器

​

iterator和const_iterator的实现其实就用到了const修饰, 来区分两种迭代器重载。

如下是list中迭代器的实现部分代码:

        (只需关注const修饰成员函数来形成重载这种思想。 list迭代器的具体实现不需要关注,后续会有专门一节来实现list的stl)

///迭代器
		iterator begin() 
		{
			return iterator(_head->_next);
		}

		iterator end() 
    	{
			return iterator(_head);
		}

///只读迭代器。 如果没有这个const修饰, 他和上面的迭代器构不成重载
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		const_iterator end() const
		{
			return const_iterator(_head);
		}

注意红框框。

const比较容易出现问题的地方

如图是vector的标准库中的size()和capacity()的模拟实现

​

public:

   //………………………………public中的其他成员
​
		size_t size() const
		{
			return _finish - _start;
		}

		size_t capacity() const
		{
		    return _end_of_storage - _start;
	    }

​   //……………………………………public中的其他成员

private:
        T* _start;
        T* _finish;
        T* _end_of_storage;

​

这两个成员函数的实现必须使用const修饰。 为什么呢?因为size()和capacity是只读的。 他们不涉及成员变量的修改。  注意, 这里很重要。 也有点难以理解。 

我们说如果一个对象是常量。 那么这个常量对象只能调用const修饰的成员函数, 这里没问题吧。

然后, 假如, size()和capacity()没有被const修饰, 那么假如我们想计算常量对象的大小时, 是不是就无法调用这两个函数。 所以这显然是不对的。而且, 这个常量对象, 我们是不是一定不会去修改它?所以也就不需要调用那些涉及修改数值的函数。 那么, 那些涉及修改数值的函数, 就不需要使用const修饰。 因为我们不会让const对象去调用他们。

但是, 如果一些不涉及修改数值的函数, 我们在使用const对象时, 是不是可能去调用这种不修改数值的函数。 这些函数如果没有使用const修饰的话。 const对象就调不动他们。 这不符合我们的需求。所以我们必须给不修改数值的成员函数加const修饰。

由以上分析, 我在这里总结一个结论:设计修改数值的成员函数, 按照需要看是否加const修饰成员函数。 不涉及修改数值的成员函数, 一定要加const修饰成员函数。

这个结论正在随着我代码量的提高逐渐深刻。 当涉及容器的套娃时,可能会出现例外。 但是本人认为可以加深对于成员函数, 成员变量之间关系的理解。

 static

static在c++中的作用

        static在c++用和在c语言中的应用场景类似。 都是用来修饰变量或者函数。 这里的变量或者函数包括类的成员变量和类的成员函数。

在全局作用域修饰变量或者函数

        static在全局作用域修饰变量或者函数时, 这个变量或者函数属于整个源文件。 但是不属于项目中的其他源文件。 其他源文件无法调用static在全局修饰的成员或者函数。 

在局部修饰变量或者函数

        在局部修饰变量的时候, static修饰的变量生命周期变长,局部作用域销毁后,这个变量不会销毁。 但是使用的范围不变。 这个变量仍然只能在局部使用。也就是说, static修饰的成员的作用域是当前作用域, 但是生命周期变成了整个程序的生命周期。 它在程序运行时创建, 在程序结束时销毁。 

static修饰类中成员

以上都是static修饰非类中的成员。

现在来看static修饰类中的成员。 

static修饰成员变量
static修饰成员变量的性质

static修饰的成员变量不是属于对象的。 是属于整个类的。 它同样符合上面的性质:作用域属于当前作用域,也就是类域。 生命周期是整个程序的周期。

static成员变量与普通成员变量的区别

static成员变量会在程序开始运行时就被创建出来。 而一般的成员变量是在对象实例化的时候被定义的。 所以成员变量可以属于一个对象, 但是static修饰的成员变量只能属于整个类域。

同时, 一般的成员函数是无法访问static修饰的成员变量的。 为什么?很简单。 这里涉及到了成员函数为什么可以访问成员变量的知识点。

看这一串代码。

​
public:

        //………………………………public中的其他成员
    	void operator=(const vector<T>& v) 
		{
			T* tmp = new T[v.size()];
			for (size_t i = 0; i < v.size(); i++) 
			{
				tmp[i] = v[i];
			}
			_start = tmp;
			_finish = _start + (v._finish - v._start);
			_end_of_storage = _start + v.capacity();


		}

        //…………………………public中的其他成员

private:
        T* _start;
        T* _finish;
        T* _end_of_storage;

​

 看绿框框和红框框。 绿框框中的_finish是调用成员函数的对象的成员变量。 它是this指针找到的。这里this指针隐藏了, 其实应该本质是this->_finish, 或者*this._finish.

 红框框中的_finish和_start是v这个对象找到的。 

我们可以把this指针和v比作一个采花许可证, 然后成员函数里面比作一个花园。然后成员变量就是花园里的一朵花。 只有我们在花园中, 而且我们有采花许可证的情况下。 我们才可以采到一朵花。

这里同样的也是这么一个问题。 只有我们在成员函数中, 或者说是类域中。 并且我们有this指针或者直接有一个有一个对象。 我们才可以访问thiis指针指向的变量或者对象本身的变量。 

但是,static不属于任何一个对象。 没有任何一个对象有他的指针。 他就无法被对象访问到。 即使这个对象到了花园。

那么, 有什么方法可以访问到static成员变量呢?

static修饰成员函数

static修饰的成员函数可以用来访问static修饰的成员变量。 它是专门用来访问成员变量的。 

但是,static修饰的成员函数只能访问它的参数、类的静态数据成员和全局变量,而不能访问非静态数据成员。 所以, static也可以用来限制某个函数。 

具体看一下实例:


class AA 
{

public:
	AA()
	{}

	static void SFunc() 
	{
		cout << count1 << endl;
	}

private:
	static int count1;
};

int AA::count1 = 1;


int main() 
{
	AA().SFunc();
	return 0;
}

 图中红框框是我们定义的一个静态成员。然后, static修饰的成员变量要在类外面初始化。  这里蓝框框将它在外面初始化。 

绿框框是我们通过静态成员函数访问count1成员。 

以上, 就是本节全部内容。  

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

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

相关文章

统计-R(相关系数)与R^2(决定系数)

1.相关系数&#xff08;R&#xff09; 定义&#xff1a;考察两个事物&#xff08;在数据里我们称之为变量&#xff09;之间的相关程度。 假设有两个变量X&#xff0c;Y&#xff0c;那么两个变量间的皮尔逊相关系数可通过以下公式计算&#xff1a; 公式一&#xff1a; 其中…

机器人可反向驱动能力与力控架构

反向驱动性是电机传动系统的机械特性&#xff0c;它描述了运动是否可以轻松反转 。特别是&#xff0c;反向驱动能力取决于两个因素&#xff1a;传动运动效率和整体执行器机械阻抗。反向运动中传动装置的低运动效率意味着所施加的外力的大部分被运动反作用力抵消。然而&#xff…

蓝桥杯-python-递归

递归&#xff1a;通过自我调用解决问题的函数 注意&#xff1a; #1.递归出口 #2.当前问题如何变成子问题 例子&#xff1a;利用递归写一个阶乘函数&#xff0c;F(n),求n的阶乘 def f(n):if n < 1:return 1ans n * f(n-1)return ans print(f(5)) 例子&#xff1a;汉诺塔…

云手机的数据安全有保障吗?

随着移动互联网的迅速发展&#xff0c;云手机作为一种新兴的移动终端技术&#xff0c;正在逐渐受到人们的关注和应用。然而&#xff0c;对于云手机而言&#xff0c;数据安全问题一直是人们关注的焦点之一。本文将探讨云手机的数据安全性&#xff0c;并分析其是否具备足够的保障…

信息系统项目管理师019:存储和数据库(2信息技术发展—2.1信息技术及其发展—2.1.3存储和数据库)

文章目录 2.1.3 存储和数据库1.存储技术2.数据结构模型3.常用数据库类型4.数据仓库 记忆要点总结 2.1.3 存储和数据库 1.存储技术 存储分类根据服务器类型分为&#xff1a;封闭系统的存储和开放系统的存储。封闭系统主要指大型机等服务器。开放系统指基于包括麒麟、欧拉、UNIX…

语音识别:whisper部署服务器(远程访问,语音实时识别文字)

Whisper是OpenAI于2022年发布的一个开源深度学习模型&#xff0c;专门用于语音识别任务。它能够将音频转换成文字&#xff0c;支持多种语言的识别&#xff0c;包括但不限于英语、中文、西班牙语等。Whisper模型的特点是它在多种不同的音频条件下&#xff08;如不同的背景噪声水…

ElasticSearch文档操作[ES系列] - 第502篇

历史文章&#xff08;文章累计500&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 《…

Tomcat(二)

一、搭建个人博客 二、状态页 默认的管理页面被禁用&#xff0c;启用方法如下 修改conf/conf/tomcat-users.xml 2.1 开启状态页&#xff08;本地访问&#xff09; 2.2 开启允许远程登录状态页 2.3 host manager

机器学习_聚类(k-means)

文章目录 聚类步骤k-means APIKmeans性能评估指标Kmeans性能评估指标API 聚类步骤 k-means通常被称为劳埃德算法&#xff0c;这在数据聚类中是最经典的&#xff0c;也是相对容易理解的模型。算法执行的过程分为4个阶段。 1.首先&#xff0c;随机设K个特征空间内的点作为初始的…

基于Spring Boot的社区垃圾分类管理平台的设计与实现

摘 要 近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;社区垃圾分类管理平台利用计算机网络实现信息化管理&#xff0c;使整个社区垃圾分类管理的发展和服务水平有显著提升。 本文拟…

RediSearch比Es搜索还快的搜索引擎

1、介绍 RediSearch是一个Redis模块&#xff0c;为Redis提供查询、二次索引和全文搜索。要使用RediSearch&#xff0c;首先要在Redis数据上声明索引。然后可以使用重新搜索查询语言来查询该数据。RedSearch使用压缩的反向索引进行快速索引&#xff0c;占用内存少。RedSearch索…

Java实现定时发送邮件(基于Springboot工程)

1、功能概述&#xff1f; 1、在企业中有很多需要定时提醒的任务&#xff1a;如每天下午四点钟给第二天的值班人员发送值班消息&#xff1f;如提前一天给参与第二天会议的人员发送参会消息等。 2、这种定时提醒有很多方式如短信提醒、站内提醒等邮件提醒是其中较为方便且廉价的…

放慢音频速度的三个方法 享受慢音乐

如何让音频慢速播放&#xff1f;我们都知道&#xff0c;在观看视频时&#xff0c;我们可以选择快进播放&#xff0c;但是很少有软件支持慢速播放。然而&#xff0c;将音频慢速播放在某些情况下是非常必要的。例如&#xff0c;当我们学习一门新语言时&#xff0c;我们可以将音频…

【数据挖掘】实验3:常用的数据管理

实验3&#xff1a;常用的数据管理 一&#xff1a;实验目的与要求 1&#xff1a;熟悉和掌握常用的数据管理方法&#xff0c;包括变量重命名、缺失值分析、数据排序、随机抽样、字符串处理、文本分词。 二&#xff1a;实验内容 【创建新变量】 方法1&#xff1a; mydata <…

还原wps纯粹的编辑功能

1.关闭稻壳模板&#xff1a; 1.1. 启动wps(注意不要乱击稻壳模板&#xff0c;点了就找不到右键菜单了) 1.2. 在稻壳模板选项卡右击&#xff1a;选不再默认展示 2.关闭托盘中wps云盘图标&#xff1a;右击云盘图标/同步与设置&#xff1a; 2.1.关闭云文档同步 2.2.窗口选桌面应用…

VSCode下使用github初步

由于各种需要&#xff0c;现在需要统一将一些代码提交搞github&#xff0c;于是有了在VSCode下使用github的需求。之前只是简单的使用git clone&#xff0c;代码提交这些用的是其他源代码工具&#xff0c;于是得学习实操下&#xff0c;并做一记录以备后用。 安装 VSCode安装 …

java的成员变量和局部变量

1、什么是成员变量和局部变量&#xff1f; 2、成员变量和局部变量区别 区别 成员变量 局部变量 类中位置不同 类中方法外 方法内或者方法声明上 内存中位置不同 堆内存 栈内存 生命周期不同 随着对象的存在而存在&#xff0c;随着对象的消失而消失 随着方法的调用而…

基础:TCP三次握手做了什么,为什么要握手?

1. TCP 三次握手在做些什么 1. 第一次握手 &#xff1a; 1&#xff09;握手作用&#xff1a;客户端发出建立连接请求。 2&#xff09;数据处理&#xff1a;客户端发送连接请求报文段&#xff0c;将SYN位置为1&#xff0c;Sequence Number为x;然后&#xff0c;客户端进入SYN_S…

【DataWhale学习笔记-蝴蝶书共读】大语言模型背后

从图灵测试到ChatGPT 1950年&#xff0c;艾伦•图灵(Alan Turing)发表论文《计算机器与智能》&#xff08; Computing Machinery and Intelligence&#xff09;&#xff0c;提出并尝试回答“机器能否思考”这一关键问题。在论文中&#xff0c;图灵提出了“模仿游戏”&#xff…

CTF题型 Http请求走私总结Burp靶场例题

CTF题型 Http请求走私总结&靶场例题 文章目录 CTF题型 Http请求走私总结&靶场例题HTTP请求走私HTTP请求走私漏洞原理分析为什么用前端服务器漏洞原理界定标准界定长度 重要!!!实验环境前提POST数据包结构必要结构快速判断Http请求走私类型时间延迟CL-TETE-CL 练习例题C…
最新文章