C++入门全集(4):类与对象【下】

一、再谈构造函数

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;
};

虽然调用构造函数后,各个成员变量已经有了一个初始值,但是对于这种在函数体内赋值的语句不能称为对成员变量的初始化,只能将其称为赋初值。

因为初始化只能有一次,而构造函数体内可以写多个赋值的语句,例如:

1.2 初始化列表

像这样,被const修饰的成员变量,必须在声明时就进行初始化

虽然C++11允许我们给成员变量一个缺省值,但是C++11以前是怎么做的呢?

这里引入一个新的概念:初始化列表

初始化列表定义在构造函数的函数名和函数体之间,以一个冒号开始,接着是一个用逗号分隔的数据成员列表,每个成员变量后面跟着一个放在括号中的初始值或表达式,例如:

Date(int year, int month, int day)
	: _year(year)
	, _month(month)
	, _day(day)
{}

像这样,才能称之为真正的初始化

但是,如果一个成员变量既给了缺省值,又在初始化列表中显式定义了,那它最后的值如何呢?

通过监视窗口我们可以观察到:

如果一个成员变量既有缺省值又在初始化列表中定义了,那么就按照初始化列表中的值进行初始化

如果一个成员变量有缺省值,但是没在初始化列表中定义,那么就用它的缺省值初始化

如果一个成员变量既没有缺省值又没在初始化列表中定义,那么就给一个随机值

初始化列表有以下几个特点:

(1)每个成员变量在初始化列表中只能出现一次,因为只能初始化一次

(2)类中的以下几个成员变量,必须放在初始化列表中进行初始化

  • 引用类型的成员变量
  • const成员变量
  • 没有默认构造函数的自定义类型成员变量

原因如下: 

对于引用类型的成员变量,我们必须在声明变量时就给出它的引用对象

对于const成员变量前面已经提到过了,也是必须在声明时就初始化

对于没有默认构造函数的自定义类型成员变量,我们在初始化时需要传参

(3)尽量多使用初始化列表去初始化,因为不管你是否使用,对于自定义类型成员变量都一定会先使用初始化列表去初始化

例如:

可以看到,Date类里有一个Time类的成员函数,虽然我们没有在初始化列表中初始化它,它也会使用初始化列表去调用自己的默认构造函数

如果该自定义类型成员变量的构造函数不是无参的或者全缺省的,我们就需要手动将该变量添加至初始化列表中并给出参数。

总之,我们平时写构造函数的时候尽量用初始化列表来初始化成员变量即可

(4)成员变量在类中的声明顺序就是它在初始化列表中的初始化顺序,与其在初始化列表中的先后顺序无关

例如:

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();
}

最后打印的结果是什么呢?

可能很多人认为是 1 1,实际上是:

因为_a2比_a1先声明,所以在初始化列表中也是_a2先初始化

最后,拷贝构造函数因为也是构造函数,所以它也有初始化列表 

1.3 explicit关键字

我们知道,内置类型变量在发生类型转换的时候会生成一个临时的常性变量,例如:

int a = 1;
double b = a;

但是,类型转换不止能发生在内置类型中,内置类型也可以转换成自定义类型,这里就和构造函数扯上关系了。

一个类的构造函数,不仅起到初始化成员变量的作用,对于单个参数或第一个参数无缺省值的半缺省构造函数来说,它还具有类型转换的作用

或许没看懂,那就举一个例子

像这样,对象aa1在创建时正常调用构造函数,aa2又是怎么回事?为什么一个自定义类型能被内置类型初始化?

前面说到,内置类型在发生类型转换的时候会生成一个临时的常性变量,这里也是一样

首先编译器使用1作为参数调用构造函数,创建一个临时变量,再用这个临时变量调用拷贝构造函数对aa2赋值

所以aa1只调用了一次构造函数,而aa2这一行代码调用了一次构造函数和一次拷贝构造函数

这种方式既影响代码可读性,又增加了消耗,有什么办法可以禁止构造函数类型转换呢?

这里引入explicit关键字,在构造函数的前面加上它,即可禁止类型转换了

这是单参数的构造函数,对于第一个参数无缺省值的半缺省构造函数也是同理

只要是只传递一个参数的构造函数,用这种方式都会发生类型转换

另外,对于需要传递多个参数的构造函数,在C++11后也开始支持类型转换了,例如:

如果不想类型转换,用explicit修饰构造函数即可


二、static成员

2.1 概念

static修饰的类成员称为类的静态成员,static修饰的成员变量称为静态成员变量,static修饰的成员函数称为静态成员函数

有一道面试题:实现一个类,计算程序中创建过多少个类对象

可以看到,使用静态成员变量和静态成员函数可以很轻松的解决这个问题

2.2 特性

根据上面的图,我们可以得出以下几点 

(1)静态成员不属于某个对象,而是属于所有对象、属于整个类,存放在静态区

所以我们上面可以直接使用类名和作用域限定符来访问静态成员函数GetCount,当然也可以创建一个对象来访问静态成员函数,但是有点多此一举了

2)静态成员变量必须在类外进行定义和初始化,不需要添加static,在类中声明时才需要加

(3)静态成员函数没有隐式的this指针形参,所以不能访问任何非静态成员

(4)静态成员也是类的成员,受public、protected和private访问限定符的限制

像上面,因为_count是私有的,我们只能用函数来获取它,如果它是公有的,我们也可以直接访问


三、友元

当我们在类外定义了一个函数,想要访问类中的私有成员变量怎么办呢?这里就涉及到友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用

友元又分为:友元函数友元类

3.1 友元函数

我们知道,cout和流插入运算符可以实现将内置类型打印的效果,那么假设我想将流插入运算符重载,让自定义类型也可以使用呢?

我们试试在类中实现流插入运算符的重载函数

可以看到,实现的重载函数没有起效

这是因为,成员函数的第一个变量是this指针,在重载函数中对应第一个变量的是第一个操作数

所以如果像这样就可以正常运行了

但是这样也太怪了吧,和平时用cout一点也不一样,有没有别的办法呢?

我们只好在类外去实现流插入运算符的重载函数了,但是类外的函数又没办法访问类内的私有成员

像这种必须定义在类外,但是又需要访问类内的私有成员的函数,就需要友元来解决了

友元函数可以直接访问类内的私有成员,需要在类的内部进行声明,声明时需要加friend关键字

需要说明以下几点:

  • 虽然友元函数可以访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类的任何地方声明,不受类访问限定符限制
  • 一个函数可以同时是多个类的友元函数
  • 友元函数的调用和普通函数一样

3.2 友元类

和友元函数类似,我们也可以在类中声明一个友元类

友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的非公有成员

需要注意以下几点:

  • 友元关系是单向的,不具有交换性

比如上面,Date类是Time类的友元,所以可以直接在Date类中访问Time类的私有成员变量;

但是不代表Time类是Date类的友元,不能在Time类中访问Date类的私有成员变量

  • 友元关系不能传递

例如A是B的友元,B是C的友元,不代表A就是C的友元了

  • 友元关系不能继承

这里在讲到继承后再给大家详细介绍


四、内部类

4.1 概念

如果一个类定义在另一个类的内部,这个定义在内部的类就称为内部类。

内部类是一个独立的类,它不属于外部类,我们更不能通过外部类的对象去访问内部类的成员

外部类对内部类没有任何优越的访问权限。

内部类与外部类的关联在于:

(1)内部类受外部类的类域限制

例如我们想创建一个内部类类型的变量,需要用作用域限定符

(2)内部类天生就是外部类的友元,但是外部类不是内部类的友元

4.2 特性

(1)内部类定义在外部类的public、protected和private中都是可以的

(2)内部类可以直接访问外部类中的静态成员,而不需要借助外部类的对象或类名

(3)外部类的大小不包括内部类

例如

可以看到,外部类A的大小并没有包括内部类B,所以可以知道内部类的空间也是独立的


五、匿名对象

有时候我们可能只需要调用一次某个类的成员函数,为此如果特意去创建一个对象的话就太麻烦了

这里就可以用到匿名对象。我们平时创建一个对象可能是这样的:

如果要创建一个匿名对象的话,是这样的:

顾名思义,匿名对象在创建的时候是不用取名字的。

匿名对象的特点在于,它的生命周期只在这一行,一旦程序走到了下一行,就会自动调用析构函数销毁。

假设此时我们要调用一次A类中的Print函数,就可以用匿名对象去调用,而不用特意创建一个对象

对于各种一次性的对象创建,我们都可以使用匿名对象。

完.

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

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

相关文章

开源项目:智能化图像分类技术在新能源发电监控中的应用与实践

一、引言 在当今世界&#xff0c;能源的转型和升级是推动社会可持续发展的关键因素。随着技术的进步&#xff0c;新能源发电逐渐成为能源结构调整的重要力量。在众多发电方式中&#xff0c;新能源发电技术如风力、太阳能等因其清洁、可再生的特性而备受青睐。然而&#xff0c;…

百度文库旋转验证码识别

最近研究了一下图像识别&#xff0c;一直找到很好的应用场景&#xff0c;今天我就发现可以用百度的旋转验证码来做一个实验。没想到效果还挺好&#xff0c;下面就是实际的识别效果。 1、效果演示 2、如何识别 2.1准备数据集 首先需要使用爬虫&#xff0c;对验证码图片进行采…

MATLAB中sigmoid函数用法

目录 语法 说明 示例 应用 sigmoid 激活 sigmoid函数的功能是应用sigmoid激活 语法 Y sigmoid(X) 说明 sigmoid 激活运算将 sigmoid 函数应用于输入数据。此运算等效于&#xff1a; 注意 此函数将 sigmoid 运算应用于 dlarray 数据。如果要在 layerGraph 对象或 Layer …

Git 版本控制

Git 版本控制 1. About Version Control (关于版本控制)1.1. Local Version Control Systems (本地版本控制系统)1.2. Centralized Version Control Systems (集中化的版本控制系统)1.3. Distributed Version Control Systems (分布式版本控制系统) 2. 换行符的处理3. keyboard…

深入理解Docker自定义网络:构建高效的容器网络环境

目录 博客前言: 一.docker自定义网络介绍 1.docker自定义网络介绍 2.使用技术的优势 3.基本使用流程 二.实战操作 1.模式理论介绍 bridge模式(默认模式) host模式 2.模式特点 查看桥接模式的特点 查看仅主机模式的特点 3.实战操作 bridge模式 host模式 自定义网络…

Android Compose - PlainTooltipBox(已废弃)的替代方案

Android Compose - PlainTooltipBox 的替代方案 TooltipBox(positionProvider TooltipDefaults.rememberPlainTooltipPositionProvider(),tooltip {PlainTooltip {Text(/* tooltip content */)}},state rememberTooltipState(), ) {// tooltip anchorIconButton(onClick {…

EdgeX Foundry - MQTT 设备服务

文章目录 一、MQTT 设备服务1.概述2.服务配置3.协议属性4.多级 Topics4.1.异步数据4.2.命令 二、连接 MQTT 设备1.docker-comepse2.设备配置文件3.安装自定义配置4.启动 EdgeX Foundry5.创建 MQTT 设备模拟器6.访问 UI6.1. consul6.2. EdgeX Console 7.测试7.1.命令7.2.事件7.3…

【踏雪无痕的痕五】——一年级数学题映射动态规划

目录 一、背景介绍三、过程1.那是什么样的一个数学题&#xff1f;2.动态规划是个啥&#xff1f;3.为啥联系到动态规划了&#xff1f;4.拿01背包算法做个小例子练练手吧5.感受 四、总结 一、背景介绍 小编发烧并发症一周了&#xff0c;这一周从最开始的轻飘飘找不到灵魂在哪里—…

【心理】程序人生之情绪与压力篇,附心理学相关证书备考指南(心理学312统考,心理治疗师,中科院心理咨询师,家庭教育指导师,企业培训证书)

程序员生活指南&#xff08;情绪与压力篇&#xff09;之 【心理】程序人生之情绪与压力专项&#xff0c;附心理学相关证书备考指南&#xff08;心理学312统考&#xff0c;心理治疗师&#xff0c;中科院心理咨询师&#xff0c;家庭教育指导师&#xff0c;企业培训证书&#xff0…

Linux之进程信号

目录 一、概念引入 1、生活中的信号 2、Linux中的信号 二、信号处理常见方式 三、信号的产生 1、键盘产生信号 2、系统调用接口产生信号 3、软件条件产生信号 4、硬件异常产生信号 四、信号的保存 相关概念 信号保存——三个数据结构 信号集——sigset_t 信号集操…

程序员如何选择职业赛道?

程序员选择职业赛道就像是在一个充满挑战和机遇的迷宫中探索。不同的职业赛道代表着不同的路径&#xff0c;每条路径都有其独特的风景和挑战。我愿意为大家提供一些关于如何选择职业赛道的建议。本文将分为几个部分&#xff0c;包括了解自己、了解行业、职业规划、技能提升和持…

单片机独立按键控制LED状态

一、前言 这幅图是按键的抖动与时间的联系 按键抖动&#xff1a;对于机械开关&#xff0c;当机械鮑点断开、闭合时&#xff0c;由于机械触点的弹性作用&#xff0c;一个开关在闭合时不会马上稳定地接通&#xff0c;在断开时也不会一下子断开&#xff0c;所以在开关闭合及断开的…

加密与安全_探索数字证书

文章目录 Pre概述使用keytool生成证书使用Openssl生成证书 &#xff08;推荐&#xff09;证书的吊销小结 Pre PKI - 借助Nginx 实现Https 服务端单向认证、服务端客户端双向认证 PKI - 04 证书授权颁发机构&#xff08;CA&#xff09; & 数字证书 PKI - 数字签名与数字证…

matplotlib散点图

matplotlib散点图 假设通过爬虫你获取到了北京2016年3, 10月份每天白天的最高气温(分别位于列表a, b), 那么此时如何寻找出气温和随时间(天)变化的某种规律? from matplotlib import pyplot as pltx_3 range(1, 32) x_10 range(51, 82)y_3 [11,17,16,11,12,11,12,6,6,7,8…

GEE:使用双曲正切(tanh)激活函数对单波段图像进行变换(以NDVI为例)

作者:CSDN @ _养乐多_ 本文将介绍在 Google Earth Engine (GEE)平台上,对任意单波段影像进行 双曲正切(tanh)激活函数 变换的代码。并以对 NDVI 影像中像素值的变换为例。 文章目录 一、tanh激活函数1.1 tanh激活函数1.2 用到遥感图像上有什么用?二、代码链接三、完整代…

STL——queue

queue 1. 队列是一种容器适配器&#xff0c;专门用于在FIFO上下文(先进先出)中操作&#xff0c;其中从容器一端插入元素&#xff0c;另一端提取元素。 2. 队列作为容器适配器实现&#xff0c;容器适配器即将特定容器类封装作为其底层容器类&#xff0c;queue提供一组特…

Linux入门到入土

Linxu Linux 简介 Linux 内核最初只是由芬兰人林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;在赫尔辛基大学上学时出于个人爱好而编写的。 Linux 是一套免费使用和自由传播的类 Unix 操作系统&#xff0c;是一个基于 POSIX&#xff08;可移植操作系统接口&#xff09…

Vscode 使用SSH远程连接树莓派的教程(解决卡在Downloading with wget)

配置Vscode Remote SSH 安装OpenSSH 打开Windows开始页面&#xff0c;直接进行搜索PowerShell&#xff0c;打开第一个Windows PowerShell&#xff0c;点击以管理员身份运行 输入指令 Get-WindowsCapability -Online | ? Name -like OpenSSH* 我是已经安装好了&#xff0c;…

掘根宝典之c语言字符指针,指针数组,数组指针,函数指针

目录 字符指针 字符指针指向字符串 使用字符指针 例子 指针数组 数组指针 数组名和&数组名 数组名 sizeof(数组名)&#xff0c;&数组名 &数组名 &数组名错误使用方法 数组名和&数组名用于一维数组 例子1 例子2 数组名和&数组名用于二维数…

龙耀南街 喜闹元宵| 猜灯谜送汤圆蒙面K歌精彩多多!

上元南街,璀璨烟花!正月十五,你来南街闹元宵了吗? 为了更好的让游客体验碳水王国丰富多元的元宵活动,南街特此设定了:送汤圆做龙灯、猜灯谜送财气、大屏互动.好运连连、南街大舞台.有才你就来—蒙面歌王挑战赛、璀璨烟花,现场热闹欢腾~ 送汤圆: 做龙灯: 猜灯谜送财气: 大屏互…