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社…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
全面解析各类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…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
Android屏幕刷新率与FPS(Frames Per Second) 120hz
Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数,单位是赫兹(Hz)。 60Hz 屏幕:每秒刷新 60 次,每次刷新间隔约 16.67ms 90Hz 屏幕:每秒刷新 90 次,…...
欢乐熊大话蓝牙知识17:多连接 BLE 怎么设计服务不会乱?分层思维来救场!
多连接 BLE 怎么设计服务不会乱?分层思维来救场! 作者按: 你是不是也遇到过 BLE 多连接时,调试现场像网吧“掉线风暴”? 温度传感器连上了,心率带丢了;一边 OTA 更新,一边通知卡壳。…...
















