0基础入门C++之类和对象下篇

news2024/11/15 20:05:51

目录

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

注意:

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

这里是引用

  1. 以下三种类成员变量,必须放在初始化列表位置进行初始化:

引用成员变量

const成员变量

没有默认构造函数的自定义类型成员

这里不难理解,因为引用成员变量和const成员变量都必须在定义的时候初始化

对于没有默认构造函数的自定义类型成员:

因为默认生成的构造函数对内置类型不做处理,对自定义类型会去调用它对应的默认构造函数(不需要传参的构造函数都是默认构造函数),所以如果自定义类型成员没有默认构造函数我们就需要自己去初始化它。

  1. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,成员变量都会在初始化列表定义。

  2. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

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特性

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明,静态成员变量一定要在类外进行初始化
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受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关键字

说明:

  1. 友元函数可访问类的私有和保护成员,但不是类的成员函数
  2. 友元函数不能用const修饰
  3. 友元函数可以在类里面的任何地方声明,不受类访问限定符限制
  4. 一个函数可以是多个类的友元函数
  5. 友元函数的调用与普通函数的调用原理相同

4.2友元类

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

  1. 友元关系是单向的,不具有交换性。
  2. 友元关系不能传递

如果C是B的友元, B是A的友元,则不能说明C时A的友元

  1. 友元关系不能继承,在继承位置再给大家详细介绍。

5.内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。

比如这样:

class A
{
private:
	int a;
public:
	class B 
	{
	private:
		int b;
	};
};

内部类并不属于外部类,它和对应的外部类是相互独立的,只是受外部类类域的限制
对于上面那个类来说,我们想拿A中的内部类B去创建对象,这样是不行的:
在这里插入图片描述

这样才行:
在这里插入图片描述

内部类天生就是其对应的外部类的友元类。参见友元类的定义,内部类可以通过外部类的对象参数来访 问外部类中的所有成员。但是外部类不是内部类的友元。

特性:

  1. 内部类可以定义在外部类的public、protected、private都是可以的。
  2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  3. sizeof(外部类)=外部类,和内部类没有任何关系。

6.再次理解类和对象

现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。比如想要让计算机认识洗衣机,就需要:

  1. 用户先要对现实中洗衣机实体进行抽象—即在人为思想层面对洗衣机进行认识,洗衣机有什么属性,有那些功能,即对洗衣机进行抽象认知的一个过程
  2. 经过1之后,在人的头脑中已经对洗衣机有了一个清醒的认识,只不过此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面相对象的语言(比如:C++、Java、Python等)将洗衣机用类来进行描述,并输入到计算机中
  3. 经过2之后,在计算机中就有了一个洗衣机类,但是洗衣机类只是站在计算机的角度对洗衣 机对象进行描述的,通过洗衣机类,可以实例化出一个个具体的洗衣机对象,此时计算机才能洗衣机是什么东西。
  4. 用户就可以借助计算机中洗衣机对象,来模拟现实中的洗衣机实体了。

在类和对象阶段,大家一定要体会到,类是对某一类实体(对象)来进行描述的,描述该对象具有哪些属性,哪些方法,描述完成后就形成了一种新的自定义类型,用然后用该自定义类型就可以实例化具体的对象
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【springboot】springboot定时任务:

文章目录 一、文档&#xff1a;二、案例&#xff1a; 一、文档&#xff1a; 【cron表达式在线生成器】https://cron.qqe2.com/ 二、案例&#xff1a; EnableScheduling //开启任务调度package com.sky.task;import com.sky.entity.Orders; import com.sky.mapper.OrderMapper; …

CAN总线学习——物理层、数据链路层、CANopen协议

1、CAN总线介绍 1.1、CAN总线描述 (1)CAN总线支持多节点通信&#xff0c;但是节点不分区主从&#xff0c;也就是不存在一个节点来负责维护总线的通信&#xff1b;这点可以和I2C总线对对比&#xff0c;I2C是一主多从模式&#xff1b; (2)是差分、异步、串行总线&#xff0c;采用…

分布式—雪花算法生成ID

一、简介 1、雪花算法的组成&#xff1a; 由64个Bit(比特)位组成的long类型的数字 0 | 0000000000 0000000000 0000000000 000000000 | 00000 | 00000 | 000000000000 1个bit&#xff1a;符号位&#xff0c;始终为0。 41个bit&#xff1a;时间戳&#xff0c;精确到毫秒级别&a…

Python slice(切片)

在Python中&#xff0c;切片(slice)是对序列型对象(如list, string, tuple)的一种高级索引方法。普通索引只取出序列中一个下标对应的元素&#xff0c;而切片取出序列中一个范围对应的元素&#xff0c;这里的范围不是狭义上的连续片段。 切片的基本语法为&#xff1a; object…

ChromeOS 的 Linux 操作系统和 Chrome 浏览器分离

导读科技媒体 Ars Technica 报道称&#xff0c;谷歌正在将 ChromeOS 的浏览器从操作系统中分离出来 —— 让它变得更像 Linux。虽然目前还没有任何官方消息&#xff0c;但这项变化可能会在本月的版本更新中推出。 据介绍&#xff0c;谷歌将该项目命名为 "Lacros"——…

防溺水预警识别系统算法

防溺水预警识别系统旨在通过opencvpython网络模型深度学习算法&#xff0c;防溺水预警识别系统算法实时监测河道环境&#xff0c;对学生等违规下水游泳等危险行为进行预警和提醒。Python是一种由Guido van Rossum开发的通用编程语言&#xff0c;它很快就变得非常流行&#xff0…

strcat函数

目录 函数介绍&#xff1a; 函数声明&#xff1a; 具体使用&#xff1a; 注意事项&#xff1a; 字符串⾃⼰给⾃⼰追加&#xff0c;如何&#xff1f; 模拟实现strcat函数&#xff1a; 函数介绍&#xff1a; 被称为字符串的追加/链接函数&#xff0c;它的功能就是在一个字符…

gcc/linux下的c++异常实现

概述 本文不一定具有很好的说教性&#xff0c;仅作为自我学习的笔记。不妨可参阅国外大神博文C exceptions under the hood链接中包含了大量的例子。 偶有在对ELF做分析的时候看到如下图一些注释&#xff0c;部分关键字看不懂&#xff0c;比如什么FDE, unwind , __gxx_perso…

【技巧分享】如何获取子窗体选择了多少记录数?一招搞定!

Hi,大家好久不见。 我这个更新速度是不是太慢了呀&#xff0c;因为&#xff0c;最近又又又在忙&#xff0c;请大家谅解啦。 现在更新文章、视频都要花好久去考虑&#xff0c;好不容易有个灵感了&#xff0c;一搜索&#xff0c;结果发现之前都已经分享过了&#xff08;委屈脸&…

Nginx详解 第三部分:Nginx高级配置(附配置实例)

Part 3 一、网页的状态页二、Nginx第三方模块2.1 echo 模块 三、变量3.1 内置变量3.1.1 常用内置变量3.1.2 举个例子 3.2 自定义变量 四、自定义访问日志 (优化)4.1 自定义访问日志的格式4.2 自定义json 格式日志 五、Nginx压缩功能&#xff08;重要&#xff09;六、HTTPS 功能…

SMC_Interpolator2Dir反向插补运动

附加函数是&#xff1a; SMC_Interpolator2Dir_SlowTask 函数的位置&#xff1a; 输入&#xff1a; 运行 bExecute 【BOOL】 路径包 poqDataIn 指向SMC_OUTQUEUE的指针 停止 bSlow_Stop 停止BOOL 急停 bEmergency_Stop 紧急停止BOOL 单…

2023.8 - java - 多态

多态是同一个行为具有多个不同表现形式或形态的能力。 多态就是同一个接口&#xff0c;使用不同的实例而执行不同操作&#xff0c; 多态的优点 1. 可替换性2 可扩充性3. 接口性、灵活性、简化性4. 消除类型之间的耦合关系 多态存在的三个必要条件 继承重写父类引用指向子类…

Java“牵手”天猫商品历史价格信息API接口数据,天猫API接口申请指南

天猫平台商品历史价格接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取天猫商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片、最低价、当前价格、价格信息等详细信息 。 获取商品历史价格接口API是一种用于获取电商平台…

如何运行imbalanced-streams-master工程?

1 下载和安装JDK和Eclipse 这个步骤网上的教程特别多&#xff0c;在此就不特别叙述了。 2 将imbalanced-streams-master工程解压并引入 2.1 解压 特别要注意的是&#xff1a;解压的文件夹和workspace一定不要在同一个目录。 解压后的目录结构如下&#xff1a; 2.2 import…

图片换脸-->>视频换脸-->>直播换脸

资源网站&#xff1a;https://tianfeng.space/ 个人娱乐&#xff0c;切勿作恶 下载 ​ 网盘&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1DHMY1mCXpT0OtpmlvIoMKA 提取码&#xff1a;nf57 使用 下载解压后&#xff0c;打开 第一个就是你要替换的人脸&#xff0c;…

学习率调整策略

学习率是可以控制更新的步伐的。 我们在训练模型的时候&#xff0c;一般开始的时候学习率会比较大&#xff0c;这样可以以一个比较快的速度到达最优点的附近&#xff0c;然后再把学习率降下来&#xff0c; 缓慢的去收敛到最优值。学习率前期要大&#xff0c;后期要小 在学习学…

生成对抗网络(GAN):在图像生成和修复中的应用

文章目录 什么是生成对抗网络&#xff08;GAN&#xff09;&#xff1f;GAN在图像生成中的应用图像生成风格迁移 GAN在图像修复中的应用图像修复 拓展应用领域总结 &#x1f389;欢迎来到AIGC人工智能专栏~生成对抗网络&#xff08;GAN&#xff09;&#xff1a;在图像生成和修复…

PCI/PCIE总线的宏观理解

1、pcie总线协议实现的效果 (1)像访问内存一样去访问外设&#xff1b; (2)当建立好CPU地址空间到PCI/PCIE地址空间的映射关系后&#xff0c;程序访问CPU地址空间就可以达到访问PCI/PCIE地址空间的效果&#xff1b; 2、芯片地址空间 (1)32位的CPU寻址范围是4G&#xff0c;64位的…

【算法训练-链表】反转链表、区间反转链表、K个一组反转链表

从今天开始进行高频算法的训练&#xff0c;一方面训练自己的逻辑思维&#xff0c;一方面保持自己的竞争力。训练过程有这么两个基准原则&#xff1a; 首先训练题的来源呢有三个&#xff0c;首选的是三个都出现过的高频题&#xff0c;以&#xff1a;牛客101为基准分类&#xff…

渗透测试工具ZAP入门教程(2)-HUD教程

平视显示器 HUD是一种全新的与ZAP进行交互的方式。 它将安全信息叠加到你正在测试的应用程序上&#xff0c;并允许你访问关键的ZAP功能。 对于刚接触安全的人来说&#xff0c;它更易于理解&#xff0c;但同时也允许经验丰富的渗透测试人员将重点放在他们正在测试的应用程序上。…