[C++基础]-类和对象(下)
前言
作者:小蜗牛向前冲
名言:我可以接受失败,但我不能接受放弃
如果觉的博主的文章还不错的话,还请
点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。
目录
一、深入学习构造函数
1 构造函数赋值体
2 初始化列表
3 使用初始化列表的三种情形
4 explicit关键字
二、static成员
1 定义
2 特性
三.、友元
1、友元函数
2、友元类
四、内部类
五、匿名对象
六、拷贝对象时的一些编译器优化
本期学习内容:深入学习构造函数,重点学习Static成员,了解什么是友元,内部类,匿名对象;清楚拷贝对象时编译器会做那些优化
一、深入学习构造函数
1 构造函数赋值体
我们对一个类创建对象的时候,编译器会通过调用构造函数,给对象中每个成员变量一个合适的初始值,虽然在调用构造函数后,对象中已经有了一个初始值,但是我们不能将这称之为对对象成员的初始化,只能将其称之为赋初始值,这是因为初始化只能初始化一次,而构造函数体内可以多次赋值。
那么在类中到底怎么进行初始化呢?这就不得不提我们的初始化列表。
2 初始化列表
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟 一个放在括号中的初始值或表达式。
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
}
这里我们知道什么是初始化列表,但是在到底有什么用了,下面我们将细节讨论一下,要用初始化列表的3种情况。
3 使用初始化列表的三种情形
cosnt修饰的成员变量
对于cosnt修饰的变量只能初始化一次,而且在定义的时候必须初始化。
这里我在类A中写了构造函数,但是没有写初始化列表,编译器就报错了说我们没有对_a进行初始化,这也就说明构造函数(没有初始化列表)只能是是赋值能不能说是初始化。
引用成员变量
对于引用的成员变量,也在定义的时候要进行初始化,而仅仅简单的默认构造不能满足这个要求,所以就必须使用初始化列表来进行初始化。
自定义类型成员(且该类没有默认构造函数时)
在思考下面代码前,我们来理解一下什么是默认构造:
1 全缺省的构造函数
2 无参数的默认构造
3 我们没写编译器生成的
这里我们用下面代码来说明:
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
};
在类B中我定义了自定义类型A,那要对自定义类型A初始化,就要调用A的默认构造,但是我们发现A类中并没有默认构造(析构函数是半缺省的析构函数),所以我们必须要用初始化列表对A进行初始化,否则会报错。
在知道上面这种情况必须使用初始化列表,下面我们在来看一段代码:
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 随机值
这里会出现上面结果呢?
这里我们发现_a被初始化为了1,但是_b确是随机值,这是为什么呢?
首先我们要明白,成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关。
其次,我们上面在声明的时候是先声明_a2在声明_a1,而在初始化也要先初始化_a2,这里我们是用_a1初始化_a2的,但此时_a1是没有进行初始化自然是随机值,所以导致初始化后的_a2也是随机值,在初始化_a1的时候我们是传了一个1过去,所以_a1被初始化为1。
总结
- 三种情况必须用初始化列表,所以我们在定义的类的时候最好写是初始化列表,一个类最好提供默认构造最好是全缺省。
- 如果在声明的时候写了缺省值和初始化列表,初始列表优先起作用。
- 当没有初始化列表的时候,对于内置类型有缺省值用缺省值,没有就用随机值初始化,而自定义类型就要调用默认构造函数,没有就会报错。
4 explicit关键字
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。而关键字explicit具有禁止类型转换的作用。
单参数的构造
为了更好的理解,下面我们继续来看一下代码:
Date d1(2023);//隐式类型的转换Date d2 = 2023;const Date& d3 = 2023;
其中我们知道d1会调用他的构造函数进行初始化,但是d2这是怎么回事呢?
还能这样的吗?
看到这里我们很容易联想到拷贝构造的二种形式:
//这里是拷贝构造的二种形势Date d4(d1);Date d5 = d1;
但是这里我们要区分好,其实d2就是一个直接构造得来的,这里发生隐式类型的转换(先将2023转换为日期类,然后在进行拷贝构造),但这里编译器会进行优化为直接构造。而d3也是类似形成的。
如果我们不想发生隐式类型的转换就可以在构造函数的前面加上关键字explicit
多参数的构造(c++11支持)
int main()
{Date d1 = { 2022, 10, 12 };// 等价于Date d2(2022, 10, 12 );const Date& d3 = { 2022, 10, 12 };return 0;
}
但我们的构造函数有多个参数的时候,也可以写成d1上面的形式,发生隐式类型的转换。
如果我们不想发生隐私类型的转换同样可以加上explicit。
explicit Date(int year, int month, int day)//不发生隐式类型的转换
{}
二、static成员
1 定义
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
class A
{//静态成员函数static int Geta(){}
private://静态成员变量static int _a;
};
//静态成员变量的初始化
int A:: _a= 1;
2 特性
那我们为什么要定义静态的成员变量和成员函数呢?
其实就是因为静态的成员变量和成员函数会有他自己的特点:
1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。
这也就说明他不需要多次的创建,减少了不必要的消耗。
2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明。
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制
应用场景:要求类对象只能在栈上面
// 要求类对象只能在栈上面
class A
{
public:static A GetObj(int a = 0){A aa(a);return aa;}private:A(int a = 0):_a(a){}private:int _a;
};int main()
{A aa3 = A::GetObj(10);return 0;
}
三.、友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以 友元不宜多用。 友元分为:友元函数和友元类。
简单的说友元是为了解决,类的私有变量在类外不能访问的问题。
1、友元函数
这里我们可以想到我们在实现日期类对于<<和>>的重载的时候就用到了友元,当时尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的 输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作 数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成 全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。
// 友元声明(类的任意位置)friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
- 友元函数可访问类的私有和保护成员,但不是类的成员函数 。
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制 。
- 一个函数可以是多个类的友元函数。
- 友元函数的调用与普通函数的调用原理相同。
2、友元类
class Time
{friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}private:int _hour;int _minute;int _second;
};
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}private:int _year;int _month;int _day;Time _t;
};
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。 友元关系是单向的,不具有交换性。 比如Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接 访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。 友元关系不能传递 如果C是B的友元, B是A的友元,则不能说明C时A的友元。 友元关系不能继承。
四、内部类
概念:
如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类, 它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越 的访问权限。
注意:
内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访 问外部类中的所有成员。但是外部类不是内部类的友元 ,也就是当B是A的内部类,B可以通过A的对象访问A的成员,而反过来就不可以。
特性:
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
- sizeof(外部类)=外部类,和内部类没有任何关系。(这里也就是说求外部类的大小时候,不包含内部类的大小)
class MyA
{
private:static int k;int h;
public:class MyB // B天生就是A的友元{public:void foo(const MyA& a){cout << k << endl;//OKcout << a.h << endl;//OK}};
};
int MyA::k = 1;
int main()
{MyA::MyB b;b.foo(MyA());return 0;
}
五、匿名对象
C++中的匿名对象是指在创建对象时,没有将其赋值给任何变量,也没有使用任何名称引用它的对象。这种对象通常只在单个语句中使用,并且在该语句执行完成后被销毁。因为它们没有名称,所以它们只能通过创建它们的表达式来访问。例如,在以下代码中:
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{// 有名对象A aa0;A aa1(1);A aa2 = 2;// 匿名对象 --声明周期当前这一行A();A(3);
}
这里我们要注意,匿名对象的声明周期就在当前行,如果出来当前行就要被销毁。
那匿名对象有什么用吗?
总的来是就二点:
1 可以触发编译器的优化(下面会分析)
2 让代码看起来根据合理
A f2(){//A aa(11);//return aa(11);//用匿名对象触发优化return A(11);}
class Solution {
public:int Sum_Solution(int n) {//...return n;}
};int main()
{//Solution so;//so.Sum_Solution(10);//用匿名对象写起来更加舒服Solution().Sum_Solution(10);return 0;
}
六、拷贝对象时的一些编译器优化
在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还 是非常有用的。
下面代码的类A假设已经定义好了
场景1
int main()
{// 优化场景1A aa1 = 1; // A tmp(1) + A aa1(tmp) -> 优化 A aa1(1)return 0;
}
场景1本应该先发生对1进行隐式类型转换,在进行拷贝构造,但是编译器直接优化为拷贝构造,因为隐式类型的提升也是要消耗资料的。
但是我们要注意:下面怎么写就不能进行优化了。
int main()
{// 优化场景1A aa1;aa1 = 10;return 0;
}
场景2
void f1(A aa)
{}A f2()
{A aa;return aa;
}A f3()
{/*A aa(10);return aa;*/return A(10);
}int main()
{f1(A(1)); // 构造 + 拷贝构造 -> 优化 构造f1(1); // 构造 + 拷贝构造 -> 优化 构造f2(); // 构造+拷贝构造A ret = f2(); // 构造+拷贝构造+拷贝构造 ->优化 构造+拷贝构造A ret = f3(); // 构造+拷贝构造+拷贝构造 -> 优化 -> 构造return 0;
}
相关文章:

[C++基础]-类和对象(下)
前言 作者:小蜗牛向前冲 名言:我可以接受失败,但我不能接受放弃 如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。 目录 一、深入学…...

NP完全性PART1:多项式时间与形式化语言体系
算法导论第三版,CH34笔记 NP完全性 Chapter Introduction 一般来说,我们认为可以在多项式时间内求解的问题是易处理的问题,在超多项式时间内解决的问题是不易处理的问题。 下面列出的几对问题,前者可以用多项式时间算法求解&…...

685页40万字某省市场监管智慧应用一体化项目(word可编辑)
1.2.3.1 数字XX公共能力建设现状 1.2.3.1.1 数字XX通用基础应用平台现状 通用基础应用平台提供具有共性特征的跨部门、跨层级业务应用,与本项目有关的平台包括某省网上办事大厅、某省政务服务 APP 统一平台(X政通 APP)、某省公共信用信息平…...

【cutlass】cuTe 01 layout
简介 Layout将坐标映射到地址空间,其对阵列单元在内存中如何排布进行抽象,并提供了获取多维阵列的接口。用户可以以正常的方式(不需要做复杂的地址计算)写多维阵列存取代码,对于存取的不同方式可以改变Layout来实现&a…...

迁移学习
迁移学习 什么是迁移学习 迁移学习【斯坦福21秋季:实用机器学习中文版】 迁移学习(Transfer Learning)是一种机器学习方法,它通过将一个领域中的知识和经验迁移到另一个相关领域中,来加速和改进新领域的学习和解决问…...

基于session实现共享登录
基于session实现登录 1.发送短信验证码 Override public Result sendCode(String phone, HttpSession session) {//1.校验手机号是否合规if (RegexUtils.isPhoneInvalid(phone)) {//2.不合规直接返回 错误信息return Result.fail("手机号错误");}//3.如果合规生成验…...

Hudi学习笔记1
使用注意 从 0.10.0 版本开始,primaryKey 为必须的,不再支持没有主键的表。 primaryKey、primaryKey 和 type 均大小写敏感。 对于 MOR 类型的表,preCombineField 为必须的。 当设置 primaryKey、primaryKey 或 type 等 hudi 配置时&#…...

嚯——ChatGPT是很强,但也会胡说八道。。。
现在的ChatGPT确实强,但是也会一本正经的胡说八道,例如它回答“nineteen”中有12个字母、或是旗鱼是哺乳动物…… 尽管ChatGPT可以生成流畅甚至优雅的散文,轻松通过困扰了AI领域超过70年的图灵测试基准,但它也可能看起来非常愚蠢…...

Springboot常用注解总结
目录 一、什么是Spring Boot二、Spring常用注解三、Spring Boot常用注解1、SpringBootApplication2、ImportAutoConfiguration3、SpringBootConfiguration4、ImportResource5、PropertySource6、PropertySources7、Role8、Scope9、Lazy11、Profile12、DependsOn13、PostConstru…...

让chatGPT给我写一个CSS,我太蠢了
前言 CSS这东西,让AI写的确有点难度,毕竟它写出来的东西,没办法直接预览,这是其次。重要的是CSS这东西怎么描述,不好描述啊,比如我让他给我制作一个这样的效果出来,没办法描述,所以…...

华为OD题目:分奖金
分奖金 知识点栈时间限制: 1s 空间限制: 256MB 限定语言: 不限 题目描述: 公司老板做了一笔大生意,想要给每位员工分配一些奖金,想通过游戏的方式来决定每个人分多少钱。 按照员工的工号顺序,每个人随机抽取一个数字。按照工号的顺序往后排列…...

【算法题】2401. 最长优雅子数组
插: 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 坚持不懈,越努力越幸运,大家一起学习鸭~~~ 题目: 给你一个由 正 整数组成的数组 num…...

【Vue】Vue快速入门
Vue快速入门 Vue.js的引入 要先有一个vue.js文件,可以在vue官网下载,将其复制到项目中并在html页面中进行引入: 在head标签内引入,src内是vue的路径 <!-- 引入vue.js--><script language"JavaScript" s…...

二本菜鸡,颓废两年的自我救赎
大家好,我是帅地。 随着校招的结束,帅地的星球里也有不少小伙伴前来报喜,今天这篇,是星球一个颓废两年同学的自我救赎之路,我觉得他的经历和很多人一样,前两年可能就颓废了,后面才后知后觉&…...

Spring boot 常用注解
SpringBootApplication:用于启动Spring Boot应用程序的主类上,组合了Configuration、EnableAutoConfiguration和ComponentScan三个注解。 RestController :修饰类,使用RestController注解的Controller中的方法默认返回值都会以JS…...

mysql从零开始(05)----锁
全局锁 使用 # 启用全局锁 flush tables with read lock # 释放全局锁 unlock tables开启全局锁后,整个数据库就处于只读状态了,这种状态下,对数据的增删改操作、对表结构的更改操作都会被阻塞。 另外,当会话断开,全…...

《Linux 内核设计与实现》03. 进程管理
文章目录 进程描述符及任务结构分配进程描述符进程描述符的存放进程状态设置当前进程状态进程上下文进程家族树 进程创建线程在 Linux 中的实现创建线程内核线程 进程终结删除进程描述符孤儿进程 进程描述符及任务结构 内核把进程存放在任务队列(task list…...

深入探究HDFS:高可靠、高可扩展、高吞吐量的分布式文件系统【上进小菜猪大数据系列】
上进小菜猪,沈工大软件工程专业,爱好敲代码,持续输出干货。 引言 在当今数据时代,数据的存储和处理已经成为了各行各业的一个关键问题。尤其是在大数据领域,海量数据的存储和处理已经成为了一个不可避免的问题。为了应…...

GIMP制作艺术字技巧
GIMP下载官网 https://www.gimp.org/downloads/ 我使用的版本 2.10.32 字体下载 https://ziyouziti.com/index-index-all.html 下载解压之后会有otf、ttf等字体文件,需要拷贝到gimp当前用户目录 C:\Users\用户名\AppData\Roaming\GIMP\2.10\fonts GIMP绘制字…...

Redis 布隆过滤器总结
Redis 布隆过滤器总结 适用场景 大数据判断是否存在来实现去重:这就可以实现出上述的去重功能,如果你的服务器内存足够大的话,那么使用 HashMap 可能是一个不错的解决方案,理论上时间复杂度可以达到 O(1) 的级别,但是…...

云基础设施安全:7个保护敏感数据的最佳实践
导语:云端安全防护进行时! 您的组织可能会利用云计算的实际优势:灵活性、快速部署、成本效益、可扩展性和存储容量。但是,您是否投入了足够的精力来确保云基础设施的网络安全? 您应该这样做,因为数据泄露、…...

centos7安装nginx
1.配置环境 1).gcc yum install -y gcc2).安装第三方库 pcre-devel yum install -y pcre pcre-devel3).安装第三方库 zlib yum install -y zlib zlib-devel2.下载安装包并解压 nginx官网下载:http://nginx.org/en/download.html 或者 使用wget命令进行下载 wg…...

PyQt5 基础篇(一)-- 安装与环境配置
1 PyQt5 图形界面开发工具 Qt 库是跨平台的 C 库的集合,是最强大的 GUI 库之一,可以实现高级 API 来访问桌面和移动系统的各种服务。PyQt5 是一套 Python 绑定 Digia QT5 应用的框架。PyQt5 实现了一个 Python模块集,有 620 个类,…...

Java—JDK8新特性—函数式接口【内含思维导图】
目录 3.函数式接口 思维导图 3.1 什么是函数式接口 3.2 functionalinterface注解 源码分析 3.3 Lambda表达式和函数式接口关系 3.4 使用函数式接口 3.5 内置函数式接口 四大核的函数式接口区别 3.5.1 Supplier 函数式接口源码分析 3.5.2 Supplier 函数式接口使用 3.…...

【MySQL】外键约束和外键策略
一、什么是外键约束? 外键约束(FOREIGN KEY,缩写FK)是用来实现数据库表的参照完整性的。外键约束可以使两张表紧密的结合起来,特别是针对修改或者删除的级联操作时,会保证数据的完整性。 外键是指表…...

3. SQL底层执行原理详解
一条SQL在MySQL中是如何执行的 1. MySQL的内部组件结构1.1 Server层1.2 Store层 2. 连接器3. 分析器4. 优化器5. 执行器6. bin-log归档 本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。 1. MySQL的内部组件结…...

Bus动态刷新
Bus动态刷新全局广播配置实现 启动 EurekaMain7001ConfigcenterMain3344ConfigclientMain3355ConfigclicntMain3366 运维工程师 修改Gitee上配置文件内容,增加版本号发送POST请求curl -X POST "http://localhost:3344/actuator/bus-refresh" —次发送…...

逆波兰式的写法
一、什么是波兰式,逆波兰式和中缀表达式 6 *(37) -2 将运算数放在数值中间的运算式叫做中缀表达式 - * 6 3 7 2 将运算数放在数值前间的运算式叫做前缀表达式 6 3 7 * 2 - 将运算数放在数值后间的运算式叫做后缀表达式 二、生成逆波兰表达式 6 *(37) -2 生成…...

Linux系统日志介绍
Linux系统日志都是放在“/var/log”目录下面,各个日志文件的功能: /var/log/messages — 包括整体系统信息,其中也包含系统启动期间的日志。此外,mail,cron,daemon,kern和auth等内容也记录在va…...

第三十二章 React路由组件的简单使用
1、NavLink的使用 一个特殊版本的 Link,当它与当前 URL 匹配时,为其渲染元素添加样式属性 <NavLink className"list-group-item" to"/home">Home</NavLink> <NavLink className"list-group-item" to&quo…...