C++的第一道门坎:类与对象(三)

目录

一.再谈构造函数

1.1构造函数体赋值

1.2初始化列表

1.3explicit关键字

二.static成员

2.1概念

​编辑

2.2特性

三.友元

3.1友元函数

3.2友元类

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初始化列表

如何初始化呢?这就需要用到我们的初始化列表了。

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号内的初始值或表达式。

作用是会将括号内的内容初始化给成员变量

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

需要注意的是:初始化列表需要写在构造函数的函数体外面,参数列表的后面。 

下面提几个注意点

1.在这里需要注意的是,有一些成员必须放在初始化列表内进行初始化

  • 引用成员变量
  • const成员变量
  • 自定义类型成员且该类没有默认构造函数(即没有不传参就可以用的构造函数)
class A
{
public:A(int a):_a(a){}
private:int _a;
};
class B
{
public:B(int a,int ref):_ref(ref),_obj(10),_i(a){}
private:int& _ref;//无默认构造A _obj;//引用const int _i;//静态
};

在这里,它们都需要我们使用初始化列表来进行初始化。 

2.初始化列表是怎么工作的呢?他是按照什么顺序给成员初始化的呢?

class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};
int main()
{A a(1);a.Print();return 0;
}

 猜一下,这段程序会打印出来什么东西?

我们发现,_a2正常打印了,但是_a1却是随机值,这是为什么呢?

这时因为初始化列表是按照声明顺序进行初始化的,也就是说,它会先初始化_a2,再初始化_a1,而由于_a2是用_a1初始化的,而_a1此时还没有初始化,因此_a2打印结果是随机值。

3.尽量使用初始化列表初始化,因为不管是否使用初始化列表初始化,对于自定义类型的成员,一定会优先使用初始化列表初始化。

class Time
{
public:Time(int hour = 0):_hour(hour){cout << "我用了初始化列表哦" << endl;}
private:int _hour;
};
class Date
{
public:Date(int day){}
private:int _day;Time _t;
};
int main()
{Date a(10);return 0;
}

在这段代码中,Date类中含有一个Time类的成员,但是并没有在构造函数中对Time类成员初始化或赋初值,那么这段代码会运行吗?运行结果是什么呢?

 

可以看到,虽然我们没有对Time类成员进行操作,依旧使用了初始化列表对其初始化。 

1.3explicit关键字

构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用。

class A {
public:A(int value) { // 初始化代码}
};void func(A a) {// 函数体
}int main() {func(10);return 0;
}

在这段代码中,func函数中的调用了A类型的参数,但我们在main函数中传递了一个int类型的参数。但是这段代码是可以通过编译的,这时因为单个参数的构造函数具有隐式类型转换的作用。 

可以从上图看出,单个参数是有类型转换的作用的,这里我们可以将整型的10隐式转化为A类型

那么,如何禁止类型转换呢?

这就需要用到我们的explicit关键字了,它的作用是禁止隐式类型转换。

由于C++设计者认为多个参数的情况下隐式类型转换比较安全,所以一般都在单个参数的情况下使用。

在传递的是一个参数的前提下,我们可以使用explicit关键字,譬如以下场景:

  • 构造函数只有一个参数
  • 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
  • 全缺省构造参数

这里我们放出了两种上述情况,请大家注意,这里为了便于大家看就都没有注释掉,如果大家要复制到自己的编译器实践的话,需要注释掉一个。

class Date
{
public:
//注意,以下情况要注释掉一个。//1.只有一个参数explicit Date(int year):_year(year){}// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递explicit Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2022);d1 = 2023;//实际编译器背后会用2023构造一个临时对象,最后用临时对象给d1对象进行赋值
}

注释掉一个的话,也会编译失败,这是因为我们使用了explicit关键字,我们无法将2023隐式转换为Date类型。 

二.static成员

2.1概念

声明时加上static的类成员称为类的静态成员;

用static修饰的成员变量称之为静态成员变量;

用static修饰的成员函数称之为静态成员函数。

静态成员变量要在类外初始化。

做一道题:

现在我们实现一个类,要求计算程序中创建出了多少个类对象。

class A
{
public:A(){++_count;}~A(){--_count;}static int getcount(){return _count;}
private:static int _count;
};
int A::_count = 0;
int main()
{cout << A::getcount() << endl;A a;A b;cout << A::getcount() << endl;return 0;
}
2.2特性
  • 静态成员存放在静态区,不属于某个具体的对象,是所有类对象共享
  • 静态成员变量必须在类外定义定义时不添加static关键字,类中只是声明
  • 类静态成员用类名::静态成员对象.静态成员来访问
  • 静态成员函数中没有隐含的this指针,不可用于访问类内的非静态成员。(没有this)
  • 静态成员也受到访问限定符的限制

三.友元

友元是一种突破封装的方式,有时它很便利,有时会增加耦合度,从而破坏封装,所以友元不宜多用。

友元可分为:友元函数和友元类

3.1友元函数

如果我们尝试去重载operator<<操作符,就会发现一个问题,我们无法将其重载为成员函数。

为什么呢?我们根据代码来进行理解:

//<<有两个操作数,分别是cout和d1.
正常使用:cout<<d1;
//但是重载为成员函数,我们的第一个参数就是*this
//我们使用起来就要这么使用了:d1<<cout或者d1.operator<<(&d1,cout)
//这样是不符合我们的使用习惯的

 那么,我们就只能将其实现在类外,那么就又产生了一个新的问题,我们应该如何访问类内的私有成员呢?

这就需要用到我们的friend了,friend是朋友的意思,既然都是朋友了,我用用你的私有物品也没关系的吧?

因此,被friend修饰的函数,可以访问到类内的私有成员

class Date
{//友元函数不受访问限定符的限定friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& _cout,const Date& d)
{_cout << d._year << d._month << d._day;return _cout;
}istream& operator>>(istream& _cin, Date& d)
{_cin >> d._day >> d._month >> d._year;return _cin;
}

 说明:

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

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

下面我们用代码来实践一下:

//Time类
class Time
{//声明友元于Bfriend class Date; 
public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second)
{}
private:int _hour;int _minute;int _second;
};
//Date类
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}
private:int _year;int _month;int _day;Time _t;
};

友元类有以下性质:

  • 友元关系是单向的,不具有交换性。
  • 友元关系不具有传递性
  • 友元关系不可继承

通俗的形容:你把我当朋友,但我不一定把你当朋友我把你当朋友;

                    你把他当朋友,但是我可能不喜欢他,我不会跟他做朋友。

4.内部类

概念:如果一个类定义在另一个类的内部这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

注意点:

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

我们可以用如下代码来实践: 

class A
{
private:static int k;int h;
public:class B//B是A的友元{public:void foo(const A& a){cout << k << endl;cout << a.h << endl;}};
};
int A::k = 1;//静态
int main()
{A::B b;b.foo(A());return 0;
}

博主的C++专栏:点击进入CPP专栏

码字不易,如果你觉得博主写的不错,给个三联关注评论把!!! 

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

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

相关文章

内核编译版本号带有+问题

编译内核4.19.163以后 make ARCHarm64 modules_install INSTALL_MOD_PATH../aarch64_modules/ 发现 DEPMOD 4.19.246 修改 scripts/setlocalversion 把那个号给它干掉 解决问题

订单共享模式:开启你的终身财富之旅

在当今这个信息爆炸的时代&#xff0c;每个人都在寻找着属于自己的财富增长之道。而“二人订单共享结束制”作为一种全新的商业模式&#xff0c;正以其独特的魅力吸引着越来越多的目光。只需499元的终身消费&#xff0c;你便能成为平台的会员&#xff0c;开启一段与众不同的赚钱…

范闲通过MD5哈希算法破解庆帝与神庙信件的精彩解析

价值万元免费资料领取欢迎关注 公众号 数据分析螺丝钉 剧情背景 在《庆余年2》中&#xff0c;范闲与庆帝和神庙之间的权谋斗争愈演愈烈。但是其实早在第一季&#xff0c;范闲宫中在找打开箱子钥匙的时候就发现了一封秘信&#xff0c;这封信件可能隐藏着揭露叶轻眉的一些关键信…

基于Pytorch框架的深度学习EfficientNetV2神经网络中草药识别分类系统源码

第一步&#xff1a;准备数据 5种中草药数据&#xff1a;self.class_indict ["百合", "党参", "山魈", "枸杞", "槐花", "金银花"] &#xff0c;总共有900张图片&#xff0c;每个文件夹单独放一种数据 第二步&a…

Docker搭建FRP内网穿透服务器

使用Docker搭建一个frp内网穿透 在现代网络环境中&#xff0c;由于防火墙和NAT等原因&#xff0c;内网设备无法直接被外网访问。FRP (Fast Reverse Proxy) 是一款非常流行的内网穿透工具&#xff0c;它能够帮助我们将内网服务暴露给外网。本文将介绍如何在Linux服务器上使用Do…

压测工具Jmeter的使用

一、安装 下载地址&#xff1a; 国外地址&#xff1a;jmeter.apache.org&#xff08;下载会很慢&#xff0c;建议使用国内地址&#xff09; 国内地址&#xff1a;apache-jmeter-binaries安装包下载_开源镜像站-阿里云 下载好进入bin文件下&#xff0c;双击jmeter.bat 打开…

哈希传递(PTH)

使用Mimikatz进行PTH Pass The Hash 哈希传递攻击简称 PTH&#xff0c;该方法通过找到与账户相关的密码散列&#xff08;NTLLHash&#xff09;来进 行攻击。由于在Windows系统中&#xff0c;通常会使用NTLM Hash对访问资源的用户进行身份认证&#xff0c;所以该攻 击可以在不需…

算法学习笔记(7.2)-贪心算法(最大容量问题)

目录 ##问题描述 ##问题示例 ##释 ##贪心策略的确定 ##代码示例 ##正确性验证 ##问题描述 输入一个数组 ℎ&#x1d461; &#xff0c;其中的每个元素代表一个垂直隔板的高度。数组中的任意两个隔板&#xff0c;以及它们之间的空间可以组成一个容器。 容器的容量等于高度和宽…

泛型aaaaa

1、泛型的概述&#xff1a; 1.1 泛型的由来 根据《Java编程思想》中的描述&#xff0c;泛型出现的动机&#xff1a; 有很多原因促成了泛型的出现&#xff0c;而最引人注意的一个原因&#xff0c;就是为了创建容器类。泛型的思想很早就存在&#xff0c;如C中的模板&#xff0…

6个PPT素材模板网站,免费!

免费PPT素材模板下载&#xff0c;就上这6个网站&#xff0c;建议收藏&#xff01; 1、菜鸟图库 ppt模板免费下载|ppt背景图片 - 菜鸟图库 菜鸟图库是一个设计、办公、媒体等素材非常齐全的网站&#xff0c;站内有几百万的素材&#xff0c;其中PPT模板就有几十万个&#xff0c;…

[stm32]——定时器与PWM的LED控制

目录 一、stm32定时器 1、定时器简介 2、定时器分类 3、通用定时器介绍 二、PWM相关介绍 1、工作原理 2、PWM的一般步骤 三、定时器控制LED亮灭 1、工程创建 2、代码编写 3、实现效果 四、采用PWM模式&#xff0c;实现呼吸灯效果 1、工程创建 2、代码编写 3、实现效果 一、stm3…

STM32 IIC协议

本文代码使用 HAL 库。 文章目录 前言一、什么是IIC协议二、IIC信号三、IIC协议的通讯时序1. 写操作2. 读操作 四、上拉电阻作用总结 前言 从这篇文章开始为大家介绍一些通信协议&#xff0c;包括 UART&#xff0c;SPI&#xff0c;IIC等。 UART串口通讯协议 SPI通信协议 一、…

B端系统:角色与权限界面设计,一文读懂。

一、什么是角色与权限系统 角色与权限系统是一种用于管理和控制用户在系统中的访问和操作权限的机制。它通过将用户分配到不同的角色&#xff0c;并为每个角色分配相应的权限&#xff0c;来实现对系统资源的权限控制和管理。 在角色与权限系统中&#xff0c;通常会定义多个角色…

【深度揭秘GPT-4o】:全面解析新一代AI技术的突破与优势

目录 ​编辑 1.版本对比&#xff1a;从GPT-3到GPT-4&#xff0c;再到GPT-4o的飞跃 1.1 模型规模的扩展 1.2 训练数据的更新 1.3 算法优化与效率提升 1.4 案例分析 2.技术能力&#xff1a;GPT-4o的核心优势 2.1 卓越的自然语言理解 2.1.1 上下文理解能力 2.1.2 语义分…

el-table中的信息数据过长 :show-overflow-tooltip=‘true‘**

可以在 el-table-column中添加 :show-overflow-tooltip‘true’

Kotlin 2.0 重磅发布! 性能提升!新功能上线!开发者必看!

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

【Java面试】六、Spring框架相关

文章目录 1、单例Bean不是线程安全的2、AOP3、Spring中事务的实现4、Spring事务失效的场景4.1 情况一&#xff1a;异常被捕获4.2 情况二&#xff1a;抛出检查异常4.3 注解加在非public方法上 5、Bean的生命周期6、Bean的循环引用7、Bean循环引用的解决&#xff1a;Spring三级缓…

软考随记(二)

I/O系统的5种不同的工作方式&#xff1a; 程序控制方式&#xff1a; 无条件查询&#xff1a;I/O端口总是准备好接受主机的输出数据&#xff0c;或是总是准备好向主机输入数据&#xff0c;而CPU在需要时随时直接利用I/O指令访问相应的I/O端口&#xff0c;实现与外设的数据交换 …

python-求点积

【问题描述】&#xff1a;给出两个数组&#xff0c;并求它们的点积。 【问题描述】&#xff1a;输入A[1,1,1],B[2,2,2]&#xff0c;输出6,即1*21*21*26。输入A[3,2],B[2,3,3],输出-1&#xff0c;没有点积。 完整代码如下&#xff1a; alist(map(int,input().split())) blist(…

vscode 默认终端(Terminal) 为CMD,但是新建是powerShell

☆ 问题描述 vscode 默认终端&#xff08;Terminal&#xff09; 为CMD&#xff0c;但是新建是powerShell ★ 解决方案 随便设置其他为默认&#xff0c;然后再设置回来CMD为默认就行了&#xff0c;实在不行就重装vscode吧… ✅ 总结 应该是vscode的小bug