探索C++中的不变之美:const与构造函数的深度剖析
W...Y的主页😊
代码仓库分享💕
🍔前言:
关于C++的博客中,我们已经了解了六个默认函数中的四个,分别是构造函数、析构函数、拷贝构造函数以及函数的重载。但是这些函数都是有返回值与参数的。提到参数与返回值我们就会想到可以修饰它们的一个关键字const。而且关于构造函数,我们并没有将内容全部讲完,所以我们今天这篇博客就是对const关键字的讲解以及构造函数的补充!话不多说,我们直接开始。
目录
const成员
取地址及const取地址操作符重载
再谈构造函数
初始化列表
const成员
const对于我们有语言基础的人并不陌生,就是关于修饰变量使其成为一个不可修改的内容。在C++中也是如此,但是C++中类的出现,伴随的出现的就是一系列的成员函数,而被const修饰的成员函数就是const成员函数。
我们来看一下这段代码:
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << "Print()" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl << endl;
}private:
int _year; // 年
int _month; // 月
int _day; // 日
};
void Test()
{
Date d1(2022,1,13);
d1.Print();
const Date d2(2022,1,13);
d2.Print();
}
上述代码是有错的,在编译器编译时就会出现
这是为什么呢?当我们使用const修饰d2时,d2的类型就是const Date类型,而我们去调用print函数去打印时,print隐藏的函数参数其实是Date* const this,所以参数不匹配导致程序报错。
那this指针的参数应该怎么是隐藏的,所以C++规定在函数后加上const的实际意义就是在this指针前加const。
所以正确的print函数应该在函数后加上const进行修饰。
void Print() const
{
cout << "Print()const" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl << endl;
}
这样无论在自定义类型的前面是否加上const进行修饰,都可以对上述函数进行调用。所以在调用时,我们可以将一个变量的权限放小,但是绝不能进行放大。
随之又会引出一个问题:成员函数有const进行修饰,无论实参有无const都能进行调用,那我们需不需要将所以的成员函数都加上const呢?
其实是不用的,我们加上const进行修饰的this指针指向的内容不被修改,如果我们的成员函数需要修改this指针所指向的内容,我们就不用去加const。
Date operator++(int);
Date& operator+=(int day);
Date& operator-=(int day);
Date& operator++();
Date& operator--();
Date operator--(int);
比如上述的运算符重载就不用加const,因为这些都是改变this指向的内容的。
bool Date::operator>(const Date& y)
{if (_year > y._year){return true;}else if (_year == y._year && _month > y._month){return true;}else if (_year == y._year && _month == y._month && _day > y._day){return true;}return false;
}
int main()
{
Date s1();
const Date s2();
s1 < s2;//正确
s2 < s1;//报错
return 0;
}
上述代码是<的运算符重载,在之前的博客中我们已经进行了复现,但是当我们的参数类型一个被const修饰,另一个没有const修饰当我们调用此函数s2 < s1时就会出现报错,因为不能将实参的权限进行放大,也就是参数类型不匹配,所以这种类似内容的函数就必须加上const进行修饰。
void Print() const;
bool operator==(const Date& y) const;
bool operator!=(const Date& y) const;
bool operator>(const Date& y) const;
bool operator<(const Date& y) const;
bool operator>=(const Date& y) const;
bool operator<=(const Date& y) const;
int operator-(const Date& d) const;
Date operator+(int day) const;
Date operator-(int day) const;
总结:
1.能定义const的成员函数都应该定义成const,这样const成员与非const成员都可以进行调用。调用条件(权限平移)(权限缩小)。
2.要修改成员变量的函数不能定义const。
取地址及const取地址操作符重载
取地址操作运算符重载也是六大默认函数之一,通过重定义对对象进行取地址操作就是取地址操作符的重载。这两个默认成员函数一般不用重新定义 ,编译器默认会生成。为什么会是两个呢?因为有无const是有区别的,他们会形成函数重载。
Date* operator&()
{cout << "Date* operator&()" << endl;return this;}const Date* operator&()const
{cout << "const Date* operator&()const" << endl;return this;}
int main()
{// const对象和非const对象都可以调用const成员函数const Date d1(2023, 10, 31);d1.Print();Date d2(2023, 1, 1);d2.Print();cout << &d1 << endl;cout << &d2 << endl;return 0;
}
这里许多人就会有疑问,这里不会产生二义性吗?针对cout << &d2 << endl;因为d2没有被const修饰,所以既可以调用理论上来说两个函数都可以进行调用。但是C++会优先匹配最合适的类型,因为d2没有被const进行修饰,所以优先会调用没有被const修饰的函数。
如果将没有const修饰的函数进行屏蔽,两种实参照样可以进行调用。
再谈构造函数
之前我们就讲过构造函数已经将了有80%了,现在我们将构造函数中剩下的20%进行收尾。我们先来复习一下之前的构造体系:
class Date
{
public:
Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}
private:
int _year;
int _month;
int _day;
};
这是函数体内初始化,我们进行对象的初始化时就会调用此函数,当我们没有构造函数时,我们就会调用C++提供的默认构造函数进行匹配。
构造函数的特征:
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载。
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量
的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始
化一次,而构造函数体内可以多次赋值。
现在我们还有一种可以初始化的办法:
Date(int year, int month, int day):_year(year),_month(month) ,_day(day),_ref(year),_n(1){// 初始化列表}
这样的初始化我们称之为初始化列表。
初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟
一个放在括号中的初始值或表达式。
class Date
{
public:
Date(int year, int month, int day)
: _year(year), _month(month), _day(day)
{}
private:
int _year;
int _month;
int _day;
};
这样写我们照样可以进行初始化。这两种方法都可以进行初始化,他们的区别在哪呢?
上述的例子使用两种初始化都可以,但是有些成员变量就只能使用初始化列表进行初始化。因为在类中私有成员都只是声明,没有开辟空间,而特殊的成员变量只能在定义的时候进行赋值,比如:引用、const修饰……所以我们要在初始化列表进行定义。
在内置类型中构造函数将内置类型进行赋随机值,而特殊内置类型只能赋值一次所以不能再被改变,所以我们就要一次性将其赋值好!!!
class A
{
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
class B
{
public:
B(int a, int ref)
:_aobj(a)
,_ref(ref)
,_n(10)
{}
private:
A _aobj; // 没有默认构造函数
int& _ref; // 引用
const int _n; // const
};
所以引用成员变量、const成员变量、自定义类型成员(且该类没有默认构造函数时)都要进行初始化列表赋值。
当我们去定义一种自定义类型时,如果没有对应的构造函数,程序就会报错。所以当我们定义一个类嵌套在另一个类时,在创建类的构造函数时创建成全缺省参数的构造函数。
下面给大家看一个题:
class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout<<_a1<<" "<<_a2<<endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}
A. 输出1 1
B.程序崩溃
C.编译不通过
D.输出1 随机值
这道题应该选D,这是为什么呢?成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关 ,所以_a2是在私有成员中先声明的,所以在初始化中先定义_a2,因为_a1在后面所以先为随机值,所以_a2为随机值,_a1为1.
最后我们来总结一下初始化列表解决的问题:
1、必须在定义的地方显示初始化 引用 const 没有默认构造自定义成员
2、有些自定义成员想要显示初始化,自己控制
3. 尽量使用初始化列表初始化
4. 构造函数能不能只要初始化列表,不要函数体初始化
答:不能,因为有些初始化或者检查的工作,初始化列表也不能全部搞定
class Stack
{
public:Stack(int n = 2):_a((int*)malloc(sizeof(int)* n)), _top(0), _capacity(n){//...//cout << "Stack(int n = 2)" << endl;if (_a == nullptr){perror("malloc fail");exit(-1);}memset(_a, 0, sizeof(int) * n);}
当我们进行动态内存开辟时,我们就需要进行函数内外的配合,因为在初始化列表中不能进行其他操作,而在函数体内可以,为了避免开辟失败,我们需要进行指针的检查,以及其他操作。所以80-100%初始化列表搞定,还有需要用函数体,他们可以混着用
以上就是本次全部内容,感谢大家观看!!!
相关文章:

探索C++中的不变之美:const与构造函数的深度剖析
W...Y的主页😊 代码仓库分享💕 🍔前言: 关于C的博客中,我们已经了解了六个默认函数中的四个,分别是构造函数、析构函数、拷贝构造函数以及函数的重载。但是这些函数都是有返回值与参数的。提到参数与返回…...

DDoS类型攻击对企业造成的危害
超级科技实验室的一项研究发现,每十家企业中,有四家(39%)企业没有做好准备应对DDoS攻击,保护自身安全。且不了解应对这类攻击最有效的保护手段是什么。 由于缺乏相关安全知识和保护,使得企业面临巨大的风险。 当黑客发动DDoS攻击…...
深入理解JVM虚拟机第十五篇:虚拟机栈常见异常以及如何设置虚拟机栈的大小
大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。 孙哥链接:孙哥个人主页 作者简介:一个颜值99分,只比孙哥差一点的程序员 本专栏简介:话不多说,让我们一起干翻JavaScript 本文章简介:话不多说,让我们讲清楚JavaScript里边的Math 文章目…...
Rocketmq5延时消息最大时间
背景 Rocketmq5中支持延时消息的时间,通过Message.setDelayTimeSec可以设置延时消息的精确时间。 问题 当设置时间超过3天时出现异常 org.apache.rocketmq.client.exception.MQBrokerException: CODE: 13 DESC: timer message illegal, the delay time should no…...

uniapp @click点击事件在新版chrome浏览器点击没反应
问题描述 做项目时,有一个弹出选择的组件,怎么点都不出来,最开始还以为是业务逻辑限制了不能点击。后来才发现别人的电脑可以点出来,老版本的浏览器也可以点出来,最后定位到是新版的chrome就不行了 这是我的浏览器版本…...
beanDefinition读取器
编程式定义 BeanDefinition:自定义一个BeanDefinition, AbstractBeanDefinition beanDefinition BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); 设置beanClass 注册到容器中 B…...

linux 上flink单机安装详解
目录 一 准备安装包 二 解压 三 配置环境变量 四 验证是否部署成功 一 准备安装包 官网地址: Downloads | Apache Flink 百度网盘资源: 链接: https://pan.baidu.com/s/15aXmF3JLxnOlPiDxId637Q?pwdsqsx 提取码: sqsx 这里准备的版本是flink1.13…...

数据链路层中存在的报文ip,arp,rarp
IP数据报 ARP请求/应答报 RARP请求/应答报 IP数据报 这里的目的地址和源地址是MAC地址。 这个被称为 MAC 地址,是一个网卡的物理地址,用十六进制,6 个 byte 表示。 MAC 地址是一个很容易让人误解的地址。因为 MAC 地址号称全球唯一&…...

【Tricks】PC端微信输入时,文本出现右对齐情况怎么恢复
应该是摁到某个快捷键,于是光标就变成如下图所示的样子: 如果再输入字符,则字符就会变成下图所示的样子(对齐输入框右侧): 解决办法:ctrl J 解决办法:ctrl J 解决办法࿱…...

python使用requests+excel进行接口自动化测试
在当今的互联网时代中,接口自动化测试越来越成为软件测试的重要组成部分。Python是一种简单易学,高效且可扩展的语言,自然而然地成为了开发人员的首选开发语言。而requests和xlwt这两个常用的Python标准库,能够帮助我们轻松地开发…...

Android NDK开发详解之调试和性能分析的ndk-gdb
Android NDK开发详解之调试和性能分析的ndk-gdb 要求用法选项 NDK 包含一个名为 ndk-gdb 的 Shell 脚本,可以启动命令行原生调试会话。偏好使用 GUI 的用户则应阅读在 Android Studio 中调试这篇文档。 要求 要运行命令行原生调试,必须满足以下要求&am…...

html获取网络数据,列表展示 一
html获取网络数据,列表展示 js遍历json数组中的json对象 image.png || - 判断数据是否为空,为空就显示 - <!DOCTYPE html> <html><head><meta charset"utf-8"><title>网页列表</title></head><b…...

配置管理工具-Confd
1 简介 1.1 Confd介绍 Confd是一个轻量级的配置管理工具。通过查询后端存储,结合配置模板引擎,保持本地配置最新,同时具备定期探测机制,配置变更自动reload。对应的后端存储可以是etcd,redis、zookeeper等。[1] 通过…...

0基础学习PyFlink——使用DataStream进行字数统计
大纲 sourceMapSplittingMapping ReduceKeyingReducing 完整代码结构参考资料 在《0基础学习PyFlink——模拟Hadoop流程》一文中,我们看到Hadoop在处理大数据时的MapReduce过程。 本节介绍的DataStream API,则使用了类似的结构。 source 为了方便&…...

OpenCV官方教程中文版 —— 图像去噪
OpenCV官方教程中文版 —— 图像去噪 前言一、原理二、OpenCV 中的图像去噪1.cv2.fastNlMeansDenoisingColored()2.cv2.fastNlMeansDenoisingMulti() 前言 目标 • 学习使用非局部平均值去噪算法去除图像中的噪音 • 学习函数 cv2.fastNlMeansDenoising(),cv2.fa…...
AcWing 102. 最佳牛围栏(前缀和+二分+DP)
AcWing 102. 最佳牛围栏 1、问题 2、分析 (1)暴力做法 看到这道题以后,我们可以先想一个最暴力的做法,就是我们去枚举所有长度至少为 F F F的区间,然后求出这个区间的和,再求出这个区间的平均值。最后在…...

React-表单受控绑定和获取Dom元素
一、表单受控组件 1.声明一个react状态 说明:useState const [value,setValue]useState("") 2.核心绑定流程 2.1绑定react状态 <div><input value{value}type"text"></input> 2.2绑定onChange事件 说明:e.…...

python hashlib模块及实例
hashlib 模块密码加密密码撞库密码加盐 一,hashlib模块 hashlib模块是用来为字符串进行加密的模块,通过该作用就可以为用户的密码进行加密。 通过模块中的hash算法可以为任意长度的字符串加密成长度相同的一串hash值。该hash算法得到的hash值有一下几个…...

垃圾回收GC
为什么要有垃圾回收? JVM之所以要有垃圾回收,是因为它能够自动管理内存,避免内存泄漏和内存溢出的问题,垃圾回收机制会自动检测和清理不再使用的对象,释放内存空间,使得开发者不需要手动管理内存,降低了开发难度和错误风险,同时,垃圾回收还可以优化内存分配,提高程序性能和响…...

kubernetes-service微服务
目录 一、service微服务 二、Ipvs模式 三、ClusterIP 1.ClusterIP 2.headless 四、NodePort 1.NodePort 2.默认端口 五、LoadBalancer 1.LoadBalancer 2.metallb 六、ExternalName 一、service微服务 Kubernetes Service微服务是一种基于Kubernetes的微服务架构&…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...