0基础入门C++之类和对象下篇
目录
- 1.再谈构造函数
- 1.1构造函数赋值
- 1.2初始化列表
- 1.3explicit关键字
- 2.static成员
- 2.1概念
- 2.1静态成员变量
- 2.2静态成员函数
- 2.3特性
- 3.匿名对象
- 4.友元函数
- 4.1友元函数
- 4.2友元类
- 5.内部类
- 6.再次理解类和对象
1.再谈构造函数
首先我们先来回忆一下构造函数:
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。即构造函数其实就是帮我们对类的成员变量赋一个初值。
1.1构造函数赋值
下面我们来看个例子:
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为 初始化只能初始化一次,而构造函数体内可以多次赋值。
下面我们再来看一个例子:
class T
{
private:int _T1;int _T2;
};
这里的
int _T1; int _T2;是对成员变量_T1、 _T2的声明,在这里只是声明类里这样两个成员变量。
那定义又是在哪里呢?
这里是对对象整体的定义。
那么对象的每个成员变量又是什么时候定义的呢?
我想这时老铁心里肯定这样想:变量整体定义了,它的成员不都也定义了吗?成员不都是属于这个对象的吗?
下面我们再看这个例子:
在这里我们发现程序无法正常运行,大家来想一下,const修饰的变量有什么特点?
是不是const修饰的变量必须在定义的时候初始化。
我想这个时候大家一定想到了:
之前我们在讲解构造函数的时候说,C++11允许内置类型成员变量在类中声明的时候可以给缺省值。
这里程序能够运行了
但是这是C++11才提出来的,那C++11之前呢?如何解决这样的问题呢?
所以我们必须要给成员变量也找一个定义的位置,不然像const这样的成员变量不好处理。
那么成员变量定义到底在哪里呢?
1.2初始化列表
面对上述问题,我们的祖师爷还是把目标锁定在了构造函数。
在构造函数里面呢又搞了一个东西叫做——初始化列表。
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
举个例子:
对于上面类中const int _a的初始化我们就可以放在初始化列表进行处理:
class T
{public:T(int t1, int t2, int a): _T1(t1), _T2(t2), _a(a){}
private:int _T1;int _T2;const int _a = 1;
};int main()
{T t(1,2,3);return 0;
}
注意:
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 以下三种类成员变量,必须放在初始化列表位置进行初始化:
引用成员变量
const成员变量
没有默认构造函数的自定义类型成员
这里不难理解,因为引用成员变量和const成员变量都必须在定义的时候初始化。
对于没有默认构造函数的自定义类型成员:
因为默认生成的构造函数对内置类型不做处理,对自定义类型会去调用它对应的默认构造函数(不需要传参的构造函数都是默认构造函数),所以如果自定义类型成员没有默认构造函数我们就需要自己去初始化它。
-
尽量使用初始化列表初始化,因为不管你是否使用初始化列表,成员变量都会在初始化列表定义。
-
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
1.3explicit关键字
我们先举个例子:
class T
{public:T(int t1): _T1(t1){}
private:int _T1;int _T2;};
我们可以用这种方式去创建对象:
int main()
{T t(1);return 0;
}
除此之外还可以这样:
int main()
{T t2 = 1;return 0;
}
这个地方
T t2 = 1;,1是一个整型,怎么可以直接去初始化一个类对象呢?
其实这里是一个隐式类型转换。和内置类型之间的隐式类型转换转化是一样的,会产生一个临时变量。
那这里T t2 = 1;是如何转换的呢?
这里也会产生一个临时变量,这个临时变量就是用1去构造出来的一个T类型的对象,然后再用这个临时对象去拷贝构造我们的t2。
下面我们用一个小例子证明一下:
class T
{public:T(int t1): _T1(t1){cout << "T(int t)" << endl;}T(const T& t): _T1(t._T1){cout << "T(const T& t)" << endl;}
private:int _T1;
};int main()
{T t2 = 1;return 0;
}
注意:拷贝构造函数也是有初始化列表的,因为拷贝构造函数是构造函数的一个重载形式。
那我们现在运行程序,看T t2 = 1;是不是先用1调构造函数创建一个临时变量,然后再调拷贝构造构造t2。
这里确实调用了构造函数,但是并没有调用拷贝构造函数。
那问题到底出在哪里了?
其实,C++编译器针对自定义类型产生临时变量的情况,会进行优化。编译器用1构造一个对象,然后再去调拷贝构造,效率受到影响,所以优化成一步,直接拿1去构造我们要创建的对象、
。
当然,不一定所有的编译器都会优化,但是一般比较新一点的编译器在这里都会优化。
在这里想告诉大家的是:
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。
那如果我们这里不想让它支持类型转换了,有没有什么办法呢?
这就要用到一个关键字——
explicit
我们只需在对应得构造函数前面加上explicit关键字:
对于单参数的构造函数是支持这种类型转换的,那多参数的构造函数呢?
这里C++98是不支持多参数的构造函数进行隐式类型转换的。但是C++11对这块进行了扩展,使得多参数的构造函数也可以进行隐式类型转换
2.static成员
2.1概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
那么static成员有什么用呢? 我们先来看一个小题目:
实现一个类,计算程序中创建出了多少个类对象。
老铁们思考一下,可以怎么做?
首先要创建一个类对象,一定是通过构造函数或者拷贝构造创建的。那我们可以定义一个全局变量n,初值为0。然后每次调用构造函数或者拷贝构造创建对象时就让n++。
但是这种方法真的好吗?
其实是不太好的,首先这里我们定义一个全局变量,首先它可能会发生命名冲突;其次,全局变量在哪都能访问(C++讲究封装)。
我想这个时候老铁可能又想到一种方法:
我们把统计个数的这个n变量放到类里面,这样它就属于这个类域了,然后如果不想让它在类外面被访问到,我们可以把它修饰成私有的。
但是这种方法真的可行吗?
如果直接放到类里面,作为类的一个成员变量,那它就属于对象,但我们要统计程序中创建对象的个数,这样我们每次创建一个对象n就会定义一次,是不是不行啊,不能让它属于每个对象,应该让它属于整个类。
2.1静态成员变量
对于上述问题我们可以这样解决:
在它前面加一个static修饰,让它成为静态成员变量。那这样它就不再属于某个具体对象了,而是存储在静态区,为所有类对象所共享。规定静态成员变量的初始化(定义的时候赋初值)一定要在类外,定义时不添加static关键字,类中只是声明。
2.2静态成员函数
静态成员函数有一个特性:静态成员函数没有隐藏的this指针,不能访问任何非静态成员。
因为非静态成员是属于对象的,都是通过this指针去访问的,而静态成员函数是没有this指针的。
2.3特性
- 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
- 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明,静态成员变量一定要在类外进行初始化
- 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员也是类的成员,受public、protected、private 访问限定符的限制
3.匿名对象
现在有这样一个类:
class T
{public:T(int t1 = 0): _T1(t1){cout << "T(int t)" << endl;}~T(){cout << "~T()" << endl;
private:int _T1;
};
我们现在想要用这个类去创建对象,除了我们之前学的方法之外,其实我们还可以这样创建对象:
这里我们用T这个类创建了一个匿名对象。
匿名对象的特点就是没有名字,但是它的生命周期只在创建它的这一行。
但是要注意,和临时变量一样,如果我们用匿名对象去初始化一个引用的话,它的生命周期就会被延长至该引用被销毁。并且这里肯定都要加const的,因为临时变量和匿名对象都具有常性。
那匿名对象有什么用呢?
现在有一个类
Solution,里面有一个非静态成员函数Sum_Solution,我们知道想要调用类里面的非静态成员函数,是需要通过对象去调用的:
那现在有了匿名对象,我们就可以这样调用了:
4.友元函数
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元分为:友元函数和友元类
4.1友元函数
在之前的类和对象的学习中我们讲过运算符重载:
现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在 类的内部声明,声明时需要加friend关键字。
说明:
- 友元函数可访问类的私有和保护成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类里面的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用原理相同
4.2友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
- 友元关系是单向的,不具有交换性。
- 友元关系不能传递
如果C是B的友元, B是A的友元,则不能说明C时A的友元。
- 友元关系不能继承,在继承位置再给大家详细介绍。
5.内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。
比如这样:
class A
{
private:int a;
public:class B {private:int b;};
};
内部类并不属于外部类,它和对应的外部类是相互独立的,只是受外部类类域的限制。
对于上面那个类来说,我们想拿A中的内部类B去创建对象,这样是不行的:
这样才行:
内部类天生就是其对应的外部类的友元类。参见友元类的定义,内部类可以通过外部类的对象参数来访 问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
- sizeof(外部类)=外部类,和内部类没有任何关系。
6.再次理解类和对象
现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。比如想要让计算机认识洗衣机,就需要:
- 用户先要对现实中洗衣机实体进行抽象—即在人为思想层面对洗衣机进行认识,洗衣机有什么属性,有那些功能,即对洗衣机进行抽象认知的一个过程
- 经过1之后,在人的头脑中已经对洗衣机有了一个清醒的认识,只不过此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面相对象的语言(比如:C++、Java、Python等)将洗衣机用类来进行描述,并输入到计算机中
- 经过2之后,在计算机中就有了一个洗衣机类,但是洗衣机类只是站在计算机的角度对洗衣 机对象进行描述的,通过洗衣机类,可以实例化出一个个具体的洗衣机对象,此时计算机才能洗衣机是什么东西。
- 用户就可以借助计算机中洗衣机对象,来模拟现实中的洗衣机实体了。
在类和对象阶段,大家一定要体会到,类是对某一类实体(对象)来进行描述的,描述该对象具有哪些属性,哪些方法,描述完成后就形成了一种新的自定义类型,用然后用该自定义类型就可以实例化具体的对象。
相关文章:
0基础入门C++之类和对象下篇
目录 1.再谈构造函数1.1构造函数赋值1.2初始化列表1.3explicit关键字 2.static成员2.1概念2.1静态成员变量2.2静态成员函数2.3特性 3.匿名对象4.友元函数4.1友元函数4.2友元类 5.内部类6.再次理解类和对象 1.再谈构造函数 首先我们先来回忆一下构造函数: 构造函数是…...
ECMAScript 2023
从尾到头搜索数组 在 JavaScript 中,通过 find() 和 findIndex() 查找数组中的值是一种常见做法。不过,这些方法从数组的开始进行遍历: const array [{v: 1}, {v: 2}, {v: 3}, {v: 4}, {v: 5}];array.find(elem > elem.v > 3); // {v:…...
爬虫实战之使用 Python 的 Scrapy 库开发网络爬虫详解
关键词 - Python, Scrapy, 网络爬虫 在信息爆炸时代,我们每天都要面对海量的数据和信息。有时候我们需要从互联网上获取特定的数据来进行分析和应用。今天我将向大家介绍如何使用 Python 的 Scrapy 库进行网络爬虫,获取所需数据。 1. Scrapy 简介 1.1 …...
【面试题】UDP和TCP有啥区别?
UDP UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就…...
字节实习后端面试总结(C++/GO)
语言 C ++, Python 哪一个更快? 答:这个我不知道从哪方面说,就是 C + + 的话,它其实能够提供开发者非常多的权限,就是说它能涉及到一些操作系统级别的一些操作,速度应该挺快。然后 Python 实现功能还是蛮快的。 补充: 一般而言,C++更快一些,因为它是一种编译型语…...
linux 自动登录SSH
自动登录SSH 每次ssh连接服务器还要输入密码,可以进行配置自动登录SSH 步骤 在SSH的client端产生一组公钥和私钥 # 算法可以使用RSA和DSA两种ssh-keygen -f 秘钥文件名 -t 使用的算法 会生成私钥文件id_rsa以及公钥文件id_rsa.pub 把公钥上传至SSH Server端的.ssh目…...
量化:pandas基础
文章目录 简介Series构造 DataFrame构造列的查改增删填充默认值 简介 pandas是 Python 的核心数据分析支持库,提供了快速、灵活、明确的数据结构。 pandas主要的两种数据结构为Series和DataFrame,分别用于处理一维和二维数据。 Series Series 是一种类…...
华为云渲染实践
// 编者按:云计算与网络基础设施发展为云端渲染提供了更好的发展机会,华为云随之长期在自研图形渲染引擎、工业领域渲染和AI加速渲染三大方向进行云渲染方面的探索与研究。本次LiveVideoStackCon 2023上海站邀请了来自华为云的陈普,为大家分…...
SpringBoot注解详解:从核心到Web,从数据到测试,一网打尽
总结的了平时学习springboot常用的一些注解,方便以后开发时可以阅览回忆 springboot的常用注解可以分为以下几类: 核心注解:这些注解是springboot的基础,用于启动、配置和管理springboot应用。Web MVC注解:这些注解是…...
Java寻找奇数
1.题目描述 现在有一个长度为 n 的正整数序列,其中只有一种数值出现了奇数次,其他数值均出现偶数次,请你找出那个出现奇数次的数值。 输入描述: 第一行:一个整数n,表示序列的长度。第二行:n个…...
WinPlan经营大脑:精准预测,科学决策,助力企业赢得未来
近年,随着国内掀起数字化浪潮,“企业数字化转型”成为大势所趋下的必选项。但数据显示,大约79%的中小企业还处于数字化转型初期,在“企业经营管理”上存在着巨大的挑战和风险。 WinPlan经营大脑针对市场现存的企业经营管理难题,提供一站式解决方案,助力企业经营管理转型…...
多数据源切换以及事务处理
SpringBoot 多数据源切换(超级简单)_springboot数据源切换_Tz.的博客-CSDN博客 springboot dynamic多数据源demo以及常见切换、事务问题_一片星空~的博客-CSDN博客...
docker 重装提示 Exising installation is up to date 解决方法
Windows Docker 重装提示 Exising installation is up to date 解决方法 出现这个问题是因为卸载Docker没有卸载干净,导致无法重装 解决方法: 按下WindowR唤起命令输入界面,输入 regedit 打开注册表编辑在地址栏输入HKEY_LOCAL_MACHINE\SOFTW…...
k8s分散部署节点之pod反亲和性(podAntiAffinity)
使用背景和场景 业务中的某个关键服务,配置了多个replica,结果在部署时,发现多个相同的副本同时部署在同一个主机上,结果主机故障时,所有副本同时漂移了,导致服务间断性中断 基于以上背景,实现…...
大A的造血与吸血能力
由于大A持续不赚钱,玩家们就喜欢挑他的毛病,其中之一就是大A的持续吸血能力。网络上也已有人进行了相关统计,这里我想再次梳理。 造血能力 对2022年全部A股的披露数据进行汇总统计。我们重点关注经营性现金流、净利润、持续经营净利润、年度累…...
【数据库】使用ShardingSphere+Mybatis-Plus实现读写分离
书接上回:数据库调优方案中数据库主从复制,如何实现读写分离 ShardingSphere 实现读写分离的方式是通过配置数据源的方式,使得应用程序可以在执行读操作和写操作时分别访问不同的数据库实例。这样可以将读取操作分发到多个从库(从…...
【第三方接口】阿里云内容审核SDK的使用
1. 内容审核服务 内容安全是识别服务,支持对图片、视频、文本、语音等对象进行多样化场景检测,有效降低内容违规风险。 目前很多平台都支持内容检测,如阿里云、腾讯云、百度AI、网易云等国内大型互联网公司都对外提供了API。 目前用得较多…...
IDEA软件安装包分享(附安装教程)
目录 一、软件简介 二、软件下载 一、软件简介 IntelliJ IDEA是一款流行的Java集成开发环境(IDE),由捷克软件开发公司JetBrains开发。它专为Java开发人员设计,提供了许多高级功能和工具,使得开发人员能够更高效地编写…...
尚硅谷宋红康MySQL笔记 10-13
是记录,我不会记录的特别详细 第10章 创建和管理表 标识符命名规则 数据库名、表名不得超过30个字符,变量名限制为29个只能包含 A–Z, a–z, 0–9, _共63个字符数据库名、表名、字段名等对象名中间不要包含空格同一个MySQL软件中,数据库不能…...
【ag-grid-vue】基本使用
ag-grid是一款功能和性能强大外观漂亮的表格插件,ag-grid几乎能满足你对数据表格所有需求。固定列、拖动列大小和位置、多表头、自定义排序等等各种常用又必不可少功能。关于收费的问题,绝大部分应用用免费的社区版就够了,ag-grid-community社…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
















