C++类和对象下(初始化列表,静态成员,explicit关键字,友元)

C++类和对象下[初始化列表,静态成员,explicit关键字,友元]

  • 一.初始化列表
    • 1.为什么会有初始化列表
    • 2.初始化列表的语法形式
    • 3.没有默认构造函数的自定义成员变量
    • 4.初始化列表是成员变量定义的地方
    • 5.初始化列表可以跟函数体内定义搭配使用
    • 6.初始化列表执行的顺序
    • 7.总结+建议
  • 二.静态成员变量和静态成员函数
    • 1.static成员变量的引出
    • 2.static成员变量的特性
    • 3.对于解决以上需求: static成员变量的不足
    • 3.匿名对象
    • 4.static成员函数
    • 5.总结
    • 6.OJ 计算1+2+...+n
  • 三.explicit关键字
    • 1.一个奇怪的现象
    • 2.用途
    • 3.为什么要有explicit呢?
  • 四.友元
    • 1.友元函数
    • 2.友元类
      • 1.语法:
      • 2.实例:
      • 3.总结
    • 3.内部类
      • 1.语法
      • 2.实例
      • 3.总结:
  • OJ题的优化

一.初始化列表

1.为什么会有初始化列表

在这里插入图片描述
我们在Date中添加了两种成员变量:
分别是引用类型和const类型
为什么编译器会报错呢?
是不是因为编译器默认生成的构造函数不行呢?
那我们自己去实现一下怎么样?
在这里插入图片描述
还是不行:它说引用和const类型的对象定义时必须初始化
对啊,因为引用不能改变指向,所以必须在初始化引用的时候就要指定好对象
const类型的变量的值是不能修改的,因此初始化时也必须设好值

那么我们应该怎么办呢?

针对于这个问题C++创始人规定了初始化列表这一语法:

为了解决有些成员变量在定义的时候必须初始化!!!

2.初始化列表的语法形式

在这里插入图片描述

Date(int year = 1, int month = 2, int day = 3):_year(year), _month(month), _day(day), _ref(_year), _cint(_year){//函数体内可以继续进行代码的书写};
第一个是冒号
后面是逗号

在这里插入图片描述
这样我们的程序就可以通过了
其实初始化列表还解决了下面这个问题

3.没有默认构造函数的自定义成员变量

在这里插入图片描述
C++类和对象中(构造函数,析构函数,拷贝构造函数)详解
在这里插入图片描述
那么问题来了:
如果这个Stack类没有默认构造函数呢?
会发生编译错误
在这里插入图片描述
那么怎么办呢?

其实我们仔细想一想:
这个MyQueue类中的Stack类没有了默认构造函数,不就意味着这个Stack类在我们这个MyQueue类定义的时候必须初始化吗?

所以这个时候初始化列表就派上用场了

MyQueue(int capacity1, int capacity2,int size):_st1(capacity1),_st2(capacity2),_size(size){};

在这里插入图片描述
这样就可以解决这个问题了
其实就算是你这个Stack类有默认构造函数,但是如果我就是想自己去传参调用你的这个默认构造函数.也是可以这样做的
在这里插入图片描述
如果没有初始化列表这一语法,我们在声明的时候去调用Stack类的传参构造函数呢?
在这里插入图片描述
会直接报错

4.初始化列表是成员变量定义的地方

那么初始化列表到底是什么身份呢?
这么强大

初始化列表是成员变量定义的地方

我们之前提到过:
在这里插入图片描述
在这里插入图片描述
也就是说只要你这个类有成员变量,就一定会走构造函数,只要你走构造函数,就一定会走构造函数当中的初始化列表

而成员变量就是在初始化列表中定义的,也就是在初始化列表中分配的空间!!

5.初始化列表可以跟函数体内定义搭配使用

那么既然初始化列表这么强大,可不可以不要函数体了呢?
构造函数只留一个初始化列表不就行了吗?
当然不可以

比如:下面的Stack类

	Stack(int capacity = 1):_a((int*)malloc(sizeof(int)*capacity)),_capacity(capacity),_top(0){};

在这里插入图片描述
你这不是也可以吗,这是好的情况
万一我申请空间太大,申请失败了呢?

wzs::Stack st(1000000000000000000);

我们在这里直接申请这么大的空间
在这里插入图片描述
那么该怎么办呢?
在初始化列表里面去检查吗?
初始化列表是定义成员变量给成员变量开辟空间的地方,
你在里面检查_a是否等于空指针
你这不是大材小用吗
直接让_a在函数体内初始化它不香吗?

也就是这样

Stack(long long capacity = 1):_capacity(capacity),_top(0)
{_a = ((int*)malloc(sizeof(int) * capacity));if (_a == nullptr){perror("malloc fail");exit(-1);}
};
wzs::Stack st(1000000000000000000);

开辟空间失败:
在这里插入图片描述
开辟空间成功:

wzs::Stack st(10);

在这里插入图片描述
也就是说初始列表和构造函数的函数体是相辅相成的,
类似的关系:引用和指针

所以日常中:对于成员变量来说
大多数情况下我们都是用初始化列表搞定
剩下那些只能用函数体去初始化的就用函数体去搞定

6.初始化列表执行的顺序

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

大家可以看一下这个题,这是《剑指offer》上的一道题目
在这里插入图片描述
答案:
n1:随机值
n2:0
因为成员变量在类中声明次序就是其在初始化列表中的初始化顺序,
所以n1先被初始化,然后n2才被初始化
也就是说
它们在初始化列表中的执行次序如下:

n1 = n2+2;
n2 = 0;

而n1和n2在还没有被初始化时都是随机值
因此n1就是随机值,而n2被初始化为了0
在这里插入图片描述

7.总结+建议

在这里插入图片描述
这个注意当中的第一条和这个初始化列表跟函数体内定义的区别这一条:
也就是说如果有人这么写:
在这里插入图片描述
也就是说初始化列表中,成员变量只能初始化一次,因此叫做初始化列表
而函数体中,成员变量可以赋值很多次,所以在函数体中只能被称为赋值初值,而不能被称为初始化!!!

也就是说对于我们的这个构造函数来说

Date(int year = 1, int month = 2, int day = 3): _ref(_year), _cint(_year){_year=year;_month=month;_day=day;};
private:int _year;int _month;int _day=26;int& _ref;const int _cint;

本质上编译器是这样进行的:

Date(int year = 1, int month = 2, int day = 3):_year(随机值),_month(随机值),_day(缺省值26),_ref(_year), _cint(_year){//下面是重新对_year,_month,_day进行再次赋值_year=year;_month=month;_day=day;};
private:int _year;int _month;int _day=26;int& _ref;const int _cint;

也就是说构造函数中:
初始化列表是成员变量定义的地方,每一个成员变量都需要走初始化列表

如果在初始化列表当中我们没有显式定义该成员变量,就会使用该成员变量声明时给的缺省值,如果没有缺省值,那么就会使用编译器给的随机值

函数体内定义其实本质上是给成员变量进行第二次赋值

也就是像这样

int var = 随机值; //初始化列表
var=1;//函数体内定义

其实翻来覆去就只有一句话:初始化列表是成员变量定义的地方
函数体内是成员变量可以进行二次赋值的地方

二.静态成员变量和静态成员函数

在这里插入图片描述
我们出现了一个需求:计算一个类实例化出了多少个对象

1.static成员变量的引出

其实方法很简单:只需要定义一个全局变量count=0,然后在这个类的所有的构造函数当中都让这个count++
最后count的数值就是该类实例化出的对象的个数
在这里插入图片描述
这样是可行的,但是并不好
为什么呢?
因为这个变量count是一个全局变量,也就是说这个count可以在程序的任意位置访问并修改

所以有可能会出现这种情况:
在这里插入图片描述
这个func函数中它成功地把我count这个全局变量给修改了
导致最后得出的答案少了1

也就是我们无法避免这种极端情况的发生
那怎么办呢?

而且把这个count定义为成员变量也是不行的
因为每一个对象都有自己的count,都是++的自己的count

那么有没有一种成员变量是为我这个类实例化出的所有的对象所共享的呢?
static成员变量就出现了

2.static成员变量的特性

在这里插入图片描述
既然我们了解了static成员变量的特性

3.对于解决以上需求: static成员变量的不足

那么我们该怎么解决上面那个需求呢?
在这里插入图片描述
但是还是有一个问题
我在这里是把_count这个静态成员变量的访问属性设置为公有了,但是也防不住会出现下面这种情况:
在这里插入图片描述
是,你是把_count这个全局变量放到了你A这个类的内部
但是你把它的访问属性设置为了公有,
因此我func想改你这个_count,我只需要加上你A这个类域,我依然能改,你拦不住我

那怎么办呢?
把_count放到私有属性下:

3.匿名对象

在这里插入图片描述
是,我func现在是改不了了,但是你main函数也访问不了啊
怎么办呢?
1.封装get函数
在这里插入图片描述
不错,而且我func函数也无法修改你这个count了
但是你这样的前提是你这个main函数里面有一个A的对象啊
这样你才能用对象.去访问这个get函数啊

可是如果我原本就没有在这个main函数里面创建一个A类型的对象
那你就只能这样了
在这里插入图片描述
可是你说我为了能够得到这个类到底实例化出了多少个对象,还要去特意在我main函数里面特意实例化出一个对象,
太挫了吧

而且我还要考虑取名字的事情,而且我创建这个对象只需要让它完成者一个任务即可,我后续也不想用它,能不能让它在执行完这个任务之后就销毁呢?

这就可以用到匿名对象了
在这里插入图片描述
匿名对象就是A()
它的特点是:
1.不用取名字
2.它的生命周期只有

cout << wzs::A().GetCount()-1 << endl;

这一行
完美符合了我刚才的需求

请注意:但是这个-1有点碍眼,让我这个代码不是很帅
显得我这个人的水平很挫

怎么办呢?
可是想要访问这个get函数就是必须创建一个对象
然后用对象.才能访问啊

能不能不用对象呢?
于是static成员函数出现了

4.static成员函数

在这里插入图片描述
在这里插入图片描述
这样就能把-1这个影响我代码美观性地东西就消失了
到了这里我们这个需求就完美解决了

5.总结

在这里插入图片描述
到了这里,大家就能对下面这张图片有更深刻的理解了
在这里插入图片描述

6.OJ 计算1+2+…+n

下面我们来做一道OJ题来巩固一下上面的知识
求1+2+3+…+n
在这里插入图片描述
这么多限制条件,怎么办呢?
其实这个题的本意就是让我们利用类的静态成员来解决这个问题

注意:牛客网的编程题所采用的编译器是支持变长数组的,VS编译器是不支持变长数组的
在这里插入图片描述
在这里插入图片描述
但是这个代码还不是特别好,当我们介绍完内部类之后,我们还会对这个代码进行进一步修改

三.explicit关键字

1.一个奇怪的现象

在这里插入图片描述

wzs::B b2 = 1;
竟然能这么创建一个对象,这是怎么做到的呢?

其实:
我们之前在C++入门-引用中介绍过:

在这里插入图片描述
因此我们可以认为是这样进行的
在这里插入图片描述
那么下面的问题就是
为什么1这个内置类型能隐式类型转换为我B这个类类型呢?

其实这里有一个规则:

当某个类的构造函数可以只传入一个参数时,而且这个参数的类型跟我这个内置类型相同的时候
就可以发生这个类类型对象和这个内置类型变量之间的隐式类型转换下面这个例子能帮大家更好地去理解
class A
{
public:构造函数第1种情况:A(int val) //单参数构造函数:_var1(val){}         A(int val1,int val2 = 2,int val3 = 3)//半缺省,且只传一个参数即可完成对象的构造:_var1(val1),_var2(val2),_var3(val3){}A(int val1 = 1,int val2 = 2,int val3 = 3)//全缺省,这个构造函数允许只传一个参数进行构造:_var1(val1),_var2(val2),_var3(val3){}private:int _var1;int _var2;int _var3;
};
它们都允许: A a = 1;
但是当这个内置类型跟我这个参数的类型不匹配时:
例如   A a = nullptr; 这样就无法发生隐式类型转换

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
而且还有一种书写方式:列表初始化

zs::A a={2023,11,3};

在这里插入图片描述
这个本质也跟上面那个内置类型1隐式转换为A类类型的对象一样:
在这里插入图片描述
可是有一个问题啊
你跟我讲这么一大堆,有什么用呢?

2.用途

我们这里先用一下STL中的vector容器
当我们在leetcode做OJ题的时候:

#include <vector>
int main()
{vector<zs::A> v;//当我们在leetcode做OJ题时://1.在没有学习这个知识之前我们平常的做法zs::A a(1);v.push_back(a);//2.学习了匿名对象后 v.push_back(zs::A(1));//  这代码写起来爽了很多//3.学习了内置类型和自定义类型之间的隐式转换后v.push_back(1);//     这代码写起来太爽了,可是只有在构造函数允许只传一个参数的时候才可以啊//4.学习了列表初始化隐式类型转换为类类型对象后v.push_back({ 2023,11,3 });//   爽飞了  ,而且允许只传一个参数对我无效return 0;
}

在这里插入图片描述
可见这个知识是很棒的

3.为什么要有explicit呢?

那么你介绍的这个explicit是什么呢?
他有什么用呢?
在这里插入图片描述
在这里插入图片描述

四.友元

在这里插入图片描述

1.友元函数

我们之前在介绍运算符重载的时候提到过友元函数

当时是为了解决日期类的流插入和流提取运算符不能定义在类内,但是还想要访问这个类的成员变量的矛盾
详细的请况大家可以去看这篇博客:
C++类和对象中:运算符重载+const成员函数+日期类的完善
在这里插入图片描述

2.友元类

1.语法:

这里以C类是B类的友元为例
class B
{friend class C;//友元声明不受类访问限定符的限制
}
class C
{B b;//需要有一个B类的对象//然后想要访问B类的成员变量或者成员函数的时候就可以用这个B类的对象.去访问
}

2.实例:

namespace zs
{
class B
{
public:friend class C;
private:void FuncOfB(){cout << "private:  FuncOfB()调用" << endl;}int _bint = 1;static int _StaticInt;
};
int B::_StaticInt = 5;
class C
{public:void SetMemberOfB(int val){b._bint = val;B::_StaticInt = val;}void ReadMemberOfB(){cout << b._bint << endl;cout << B::_StaticInt << endl;//只能这样访问}void ReadFuncOfB(){b.FuncOfB();}private:int _cint;B b;};
}
int main()
{zs::C c;c.SetMemberOfB(100);c.ReadMemberOfB();c.ReadFuncOfB();return 0;
}

3.总结

关于友元类的访问方法其实大家只需要记住一点:

友元类:我是你的朋友,
我也只是能够访问你的私有成员而已
访问方法跟普通类访问你的公有成员的方法一样

在这里插入图片描述
注意:友元关系是单向的,不具有交换性
因此上面的类B就无法访问类C的私有成员

3.内部类

1.语法

在这里插入图片描述

2.实例

namespace zs
{//注意:D是外部类,E是内部类.E天生就是D的友元,但是默认情况下D是不能访问E的,除非在E中声明D是E的友元类class D{public:class E{public:void GetStaticMemberOfD(){cout << _StaticInt << endl;//yes  这是上述第3条特性:  内部类可以直接访问外部类的static成员,不需要外部类的对象/类名//cout << _NonStaticInt << endl;//err  内部类不能直接访问外部类的非static成员cout << d._NonStaticInt << endl;//yes  只能用对象.去访问//也就是说内部类访问外部类:只不过是静态成员可以直接访问而已,非静态成员的访问跟普通类访问外部类的非静态成员的方法一样}private:zs::D d;};private:static int _StaticInt;int _NonStaticInt = 10;};int D::_StaticInt = 1;
}

3.总结:

在这里插入图片描述
其实C++不常用内部类

OJ题的优化

学习了内部类之后,我们就能对那道OJ题进行优化
在这里插入图片描述
在这里插入图片描述

以上就是类和对象下的全部内容,希望能对大家有所帮助!!!

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

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

相关文章

当风格遇上浴缸:浴室装饰的秘诀

浴室不再仅仅是个洗漱的地方&#xff0c;如今它们是室内设计的一部分&#xff0c;有时甚至是焦点。浴室的装饰风格可以塑造整个房间的氛围&#xff0c;而浴缸通常是浴室内最引人注目的元素之一。在这里&#xff0c;我们将简单探讨不同室内设计风格与浴缸如何融合&#xff0c;让…

PlantSimulation安装帮助文档端口被占用的解决办法

PlantSimulation安装帮助文档端口被占用的解决办法 从PlantSimulaiton&#xff08;TPS&#xff09;2201开始帮助文档开始使用在线&#xff0c;如果使用本地则需要安装本地文档服务器。但是在安装过程中你可能会遇到&#xff0c;5000断开被占用的情况。解决办法如下&#xff1a…

C# 发送邮件

1.安装 NuGet 包 2.代码如下 SendMailUtil using MimeKit; using Srm.CMER.Application.Contracts.CmerInfo; namespace Srm.Mail { public class SendMailUtil { public async static Task<string> SendEmail(SendEmialDto sendEmialDto,List<strin…

java APP自动化测试AppIum

一、前言 二、Appium环境搭建 2.1 JDK安装 2.2 Android SDK安装配置 2.3 模拟器安装及配置 2.4 Appium Desktop安装及使用 2.5 Appium配置连接模拟器 三、实战基本脚本编写 3.1 创建Maven项目并配置 3.2 简单Demo 四、写在最后 一、前言 随着移动互联网的发展&#xff0c;AP…

mac录屏快捷键指南,轻松录制屏幕内容!

“大家知道mac电脑有录屏快捷键吗&#xff0c;现在录屏不太方便&#xff0c;每次都花很多时间&#xff0c;要是有录屏快捷键&#xff0c;应该会快速很多&#xff0c;可是哪里都找不到&#xff0c;有人知道吗&#xff1f;帮帮我&#xff01;” 苹果的mac电脑以其精美的设计和卓…

Java使用pdfbox进行pdf和图片之间的转换

简介 pdfbox是Apache开源的一个项目,支持pdf文档操作功能。 官网地址: Apache PDFBox | A Java PDF Library 支持的功能如下图.引入依赖 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox-app</artifactId><version>…

AIGC|把Azure Open AI和Jira集成起来,实现智能化项目管理

目录 一、Jira与Azure OpenAI介绍 二、Jira和Azure OpenAI的REST API对接 三、利用Chats插件实现对话的过程 四、总结 一、Jira与Azure OpenAI介绍 Jira是一款由澳大利亚公司Atlassian开发的项目管理工具&#xff0c;主要用于追踪问题、管理需求、构建报告和管理故障等事项…

ruby语言怎么写个通用爬虫程序?

Ruby语言爬虫是指使用Ruby编写的网络爬虫程序&#xff0c;用于自动化地从互联网上获取数据。其中&#xff0c;CRawler是一个基于文本的小型地牢爬虫&#xff0c;它被设计为可扩展&#xff0c;所有游戏数据均通过JSON文件提供&#xff0c;程序仅处理游戏引擎。除此之外&#xff…

EasyExcel复杂表头数据导入

目录 表头示例导入代码数据导出 表头示例 导入代码 Overridepublic void importExcel(InputStream inputStream) {ItemExcelListener itemExcelListener new ItemExcelListener();EasyExcel.read(inputStream, ImportItem.class, itemExcelListener).headRowNumber(2).sheet()…

OSPF 高级特性3

一、OSPF安全特性 1、OSPF报文验证&#xff1a; 区域验证模式&#xff1a;在区域下配置一致的密码才能加入同一个区域。 [r3-ospf-1-area-0.0.0.0]authentication-mode md5 1 cipher 123456 接口验证模式&#xff1a;链路两端的接口必须配置一致的密码才能建立邻居关系 [r5-Gig…

回归预测 | Matlab实现SO-CNN-SVM蛇群算法优化卷积神经网络-支持向量机的多输入单输出回归预测

Matlab实现SO-CNN-SVM蛇群算法优化卷积神经网络-支持向量机的多输入单输出回归预测 目录 Matlab实现SO-CNN-SVM蛇群算法优化卷积神经网络-支持向量机的多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.SO-CNN-SVM蛇群算法优化卷积神经网络-支持向量…

oracel处理XML时,报ORA-31011、ORA-19202。

原字段为clob&#xff0c; 查询 SELECT XMLTYPE(字段) FROM TABLE_A报错如下&#xff1a; ORA-31011: XML 语法分析失败 ORA-19202: XML 处理 LPX-00217: invalid character 12 (U000C) Error at line 1559时出错 ORA-06512: 在 "SYS.XMLTYPE", line 272 ORA-0651…

大洋钻探系列之一引子

大洋科学钻探计划自1968年启动开始&#xff0c;迄今已有50余年&#xff0c;先后经历了&#xff14;个阶段。深海钻探计划(Deep Sea Drilling Program&#xff0c;DSDP&#xff0c;1968-1983年&#xff09;、国际大洋钻探计划&#xff08;Ocean Drilling Program&#xff0c;ODP…

目标检测 图像处理 计算机视觉 工业视觉

目标检测 图像处理 计算机视觉 工业视觉 工业表盘自动识别&#xff08;指针型和数值型&#xff09;智能水尺识别电梯中电动车识别&#xff0c;人数统计缺陷检测&#xff08;半导体&#xff0c;电子元器件等&#xff09;没带头盔检测基于dlib的人脸识别抽烟检测和睡岗检测/驾驶疲…

【使用Python编写游戏辅助工具】第三篇:鼠标连击器的实现

前言 这里是【使用Python编写游戏辅助工具】的第三篇&#xff1a;鼠标连击器的实现。本文主要介绍使用Python来实现鼠标连击功能。 鼠标连击是指在很短的时间内多次点击鼠标按钮&#xff0c;通常是鼠标左键。当触发鼠标连击时&#xff0c;鼠标按钮会迅速按下和释放多次&#xf…

双十一快递“当天达”?宏电助力物流分拣系统高效运行

​众所周知&#xff0c;每年双11都是快递业务的高峰期&#xff0c;是对各大物流企业运输能力的一次大考。为了持续提升快递配送的速度&#xff0c;自动化物流仓储建设的速度也在不断的加快&#xff0c;而在一个完整的自动化物流仓储系统中&#xff0c;输送分拣设备是物流自动化…

MySql优化经验分享

一条sql的具体执行过程 连接 我们怎么查看MySQL当前有多少个连接&#xff1f; 可以用show status命令&#xff0c;模糊匹配Thread&#xff0c; Show global status like "Thread%" show global variables like wait timeout;—非交互式超时时间&#xff0c;如JDBC…

c++ | 字符串与指针的恩断情仇

我想&#xff0c;c/c中难的不是指针&#xff0c;而是其中的变化&#xff0c;尤其是思维的转变。很多东西 就是容易掉进陷阱。好在&#xff0c;你我都是善于思考的码农&#xff01; 大致情况是这样的&#xff0c;底层<–>c语言<–>c<–>应用 而数据的传输的最…

【MongoDB】Windows 安装MongoDB 6.0

一、下载安装包 安装包下载地址https://www.mongodb.com/try/download/community这里我选择的是 二、解压并安装 1、解压 这里我将压缩包解压到了D盘&#xff0c;并重命名成了mongodb&#xff0c;解压后的目录如下&#xff1a; 2、创建配置文件 在D:\mongodb下新建conf目录…

【触想智能】4U触摸工控机具有哪些优势?

工控机也叫工控主机&#xff0c;和我们常见的普通电脑主机是一样的&#xff0c;都是由CPU、主板、内存、硬盘、电源以及机箱组成的。 工控机有很多分类&#xff0c;有无风扇工控机、嵌入式工控机、上架式工控机、4U触摸工控机等。上架式工控机在市场上是比较受欢迎的&#xff0…