第四章:初阶试炼(三)---类和对象(下)


目录

前言🍏

1. 再谈构造函数🍎

1.1 构造函数体赋值

1.2 初始化列表

1.3 explicit关键字

2. Static成员🍊

2.1 概念

2.2 特性

3. 友元🍐

3.1 友元函数

3.1.1 实现自定义类型流插入

3.1.2 实现多组流插入

3.1.3 实现自定义类型流提取

3.2 友元类

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

构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量

初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

一般成员直接先声明然后对象实例化的时候再定义;有些成员必须在定义的时候初始化
例如:const只有一次修改的机会---初始化的时候


1.2 初始化列表

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

初始化列表是每个成员变量定义初始化的位置

顺序:先走初始化列表再走函数体

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成员变量

* 自定义类型成员(且该类没有默认构造函数时)

注意:自定义类型的构造函数必须写,不然报错---没法调用进行初始化
类似于 :没有钱咋吃饭?

class A
{
public:A(int a):_a(a){}
private:int _a;
};
class B
{
public:B(int a, int ref):_aobj(a),_ref(ref),_n(10){}
private:A _aobj;  // 没有默认构造函数int& _ref;  // 引用const int _n; // const 
};

class Date{
public:Date(int year, int month, int day):_year(year),_month(month),_day(day),_p((int*)malloc(sizeof(4)*10){}
private:int _year;int _month;int _day;int* _p;
}

初始化列表可以写_p这样的吗?可以

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

#include<iostream>
using namespace std;
class Time {
public:Time(int hour = 0):_hour(hour){cout << "_hour(hour)" << endl;}
private:int _hour;
};
class Date {
public:Date(int day=0){}
private:int _day;Time _t;
};
int main() {Date(1);return 0;
}


没给缺省值,就随机值初始化;给了缺省值,但是没在初始化列表定义,就给缺省值;无论给没给缺省值,在初始化列表定义了,就给列表定义的值初始化


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();
}
A. 输出1  1
B.程序崩溃
C.编译不通过
D.输出1  随机值


先a2初始化,但是a1不知道是多少,所以随机值;然后到a1初始化,a是1所以a1初始化成1

答案:D


1.3 explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。

单参数:支持影式类型转换

class Date
{
public:// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用Date(int year):_year(year){}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2022);// 用一个整形变量给日期类型对象进行赋值//不同对象之间赋值会产生临时对象// 实际编译器背后会用2023构造一个临时对象,最后用临时对象给d1对象进行拷贝构造再赋值//2023是常量,而且2023的临时对象具有常性,不能修改;//const修饰之后权限缩小,也不能修改了--->不报错const Date& d2 = 2023;return 0;
}

explicit Date(int year)

​​​​​​​


多参数:

#include<iostream>
using namespace std;
class Date
{
public://2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具//有类型转换作用Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
int main()
{Date d1 = { 2023,2 };const Date& d2 = { 2024,2 };return 0;
}


2. Static成员🍊

2.1 概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类里定义,类外进行初始化。

如何计算出A创建了多少个对象?

方法1. 统计构造函数到底调用多少次

#include<iostream>
using namespace std;
int n = 0;
class A {
public:A() {n++;}A(const A& aa) {n++;}
};
A func() {A aa;return aa;
}
int main() {A aa1;A aa2;cout << n << endl;return 0;
}

传值返回会拷贝构造(构造的重载)一个临时对象

不好的是n值可以随意修改

方法2.static成员

#include<iostream>
using namespace std;
int n = 0;
class A {
public:A() {n++;}A(const A& aa) {n++;}static int GetN() {return n;}
private:static int n;//声明
};
int A::n = 0;//相当于静态的全局的
A func() {A aa;return aa;
}
int main() {A aa1;A aa2;
//访问的2种方法cout << A::GetN() << endl;/*cout << aa1.GetN() << endl;*/return 0;
}

2.2 特性

1. 静态成员为所有类对象所共享不属于某个具体的对象,存放在静态区

2. 静态成员变量必须在类外定义定义时不添加static关键字,类中只是声明

3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问

4. 静态成员函数没有隐藏的this指针不能访问任何非静态成员

5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制


3. 友元🍐

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

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

3.1 友元函数

3.1.1 实现自定义类型流插入

尝试去重载operator<<,先试一试将operator<<重载成成员函数​​​​​​​

#include<iostream>
using namespace std;
class Date {
public:Date(int year=2024, int month=2, int day=11): _year(year), _month(month), _day(day){}void operator<<(ostream& out) {out << _year << '-' << _month << '-' << _day << endl;}
private:int _day;int _month;int _year;
};
int main() {Date d1;//本末倒置d1 << cout;return 0;
}

我们原先是想实现这种的,但是因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置this指针默认是第一个参数也就是左操作数了。

但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成

全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字

#include<iostream>
using namespace std;
class Date {
public:Date(int year=2024, int month=2, int day=11): _year(year), _month(month), _day(day){}friend void operator<<(ostream& out,const Date &d); 
private:int _day;int _month;int _year;
};
void operator<<(ostream& out, const Date& d) {out << d._year << '-' << d._month << '-' << d._day << endl;
}
int main() {Date d1;cout << d1;return 0;
}


3.1.2 实现多组流插入

和赋值不一样的是:<<是从左往右的顺序:d1先流插入,得到一个返回值(d1),d2再向返回值(d1)中插入

#include<iostream>
using namespace std;
class Date {
public:Date(int year=2024, int month=2, int day=11): _year(year), _month(month), _day(day){}friend ostream& operator<<(ostream& out, const Date& d);
private:int _day;int _month;int _year;
};
ostream& operator<<(ostream& out, const Date& d) {out << d._year << '-' << d._month << '-' << d._day << endl;return out;
}
int main() {Date d1;Date d2(2024, 3, 1);cout << d1<<d2;return 0;
}


3.1.3 实现自定义类型流提取

#include<iostream>
using namespace std;
class Date {
public:Date(int year=2024, int month=2, int day=11): _year(year), _month(month), _day(day){}friend ostream& operator<<(ostream& out,const Date& d);friend istream& operator>>(istream& in, Date& d);//不加const,因为要放到日期内进行运算
private:int _day;int _month;int _year;
};
ostream& operator<<(ostream& out,const Date& d) {out << d._year << '-' << d._month << '-' << d._day << endl;return out;
}
istream& operator>>(istream& in, Date& d) {cout << "请依次输入年月日:";in >> d._year >> d._month >> d._day;return in;
}int main() {Date d1;Date d2;cin >> d1 >> d2;cout << d1 << d2;return 0;
}


友元函数可访问类的私有和保护成员,但不是类的成员函数

友元函数不能用const修饰

友元函数可以在类定义的任何地方声明,不受类访问限定符限制

一个函数可以是多个类的友元函数

友元函数的调用与普通函数的调用原理相同


3.2 友元类

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

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

比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接

访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。

2. 友元关系不能传递

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

#include<iostream>
using namespace std;
class Time
{friend class Date; // 声明日期类为时间类的友元类,//则在日期类中就直接访问Time类中的私有成员变量
public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}private:int _hour;int _minute;int _second;
};
class Date
{
public:Date(int year = 2024, int month = 3, 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;
};
int main() {Date d1;return 0;
}


4. 内部类🍋

4.1 概念

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

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

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

3. 内部类就类似于全局域(只是受到类域和访问限定符的限制而已)


4.2 特性

特性:

1. 内部类可以定义在外部类的public、protected、private都是可以的。

2. 注意内部类可以直接访问外部类中的static成员不需要外部类的对象/类名

3. sizeof(外部类)=外部类,和内部类没有任何关系。

#include<iostream>
using namespace std;
class A {
public:class B {private:int _b1;};
private:int _a1;int _a2;
};
int main() {cout << sizeof(A) << endl;return 0;
}


#include<iostream>
using namespace std;
class A {
public:class B {public:void Func(A* p) {p->_a1++;}private:int _b1;};
private:int _a1;int _a2;
};
int main() {A aa;A::B bb;//B bb出错必须加上A::return 0;
}

5.匿名对象🍌

#include<iostream>
using namespace std;
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
int main()
{//有名对象A aa1;// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数//匿名对象A();A(10);A aa2(2);return 0;
}


6.拷贝对象时的一些编译器优化🍉

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的。

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};
void f1(A aa)
{}
A f2()
{A aa;return aa;
}
int main()
{// 传值传参A aa1;f1(aa1);cout << endl;// 传值返回f2();cout << endl;// 隐式类型,连续构造+拷贝构造->优化为直接构造f1(1);// 一个表达式中,连续构造+拷贝构造->优化为一个构造f1(A(2));cout << endl;// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造A aa2 = f2();cout << endl;// 一个表达式中,连续拷贝构造+赋值重载->无法优化aa1 = f2();cout << endl;return 0;
}

后语🍓

这里分享就告一段落了,类和对象的部分就结束了。之后我会分享一些习题来练手,请大家多多期待😚​​​​​​​

本次的分享到这里就结束了!!!

PS:小江目前只是个新手小白。欢迎大家在评论区讨论哦!有问题也可以讨论的!期待大家的互动!!!

拜托了帮帮我点赞👍+收藏⭐️+关注➕(这对我真的很重要!!!)

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

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

相关文章

HC595级联原理及实例 - STM32

74HC595的最重要的功能就是&#xff1a;串行输入&#xff0c;并行输出。其次&#xff0c;74HC595里面有2个8位寄存器&#xff1a;移位寄存器、存储寄存器。74HC595的数据来源只有一个口&#xff0c;一次只能输入一个位&#xff0c;那么连续输入8次&#xff0c;就可以积攒为一个…

Guitar Pro8.2吉他乐谱软件功能测评评价

Guitar Pro 8.2吉他乐谱软件全面评价 Guitar Pro 8.2作为一款吉他乐谱软件&#xff0c;已经得到了广大吉他手和音乐制作人的认可。作为软件评价专家&#xff0c;我对这款软件进行了全面的体验和分析&#xff0c;以下是我在易用性、功能丰富性、用户界面设计、稳定性以及性价比…

从事通讯信息类职业岗位的任职资格

通讯信息工程师&#xff0c;主要是移动核心网和固网核心网的工程切割和维护网络安全的专业工作&#xff0c;主要负责IP数据、省网和地域网络的维护。一切跟互联网打交道的事情&#xff0c;都跟这个有关系&#xff0c;都是通讯信息类岗位的工作。从事这种工作&#xff0c;需要付…

AI:135-基于卷积神经网络的艺术品瑕疵检测与修复

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带关键代码,详细讲解供大家学习,希望…

高通XBL阶段读取分区

【需求】&#xff1a; 在某些场景下&#xff0c;需要在XBL阶段读取分区数据&#xff0c;需要验证xbl阶段方案 这里主要以裸分区为例&#xff0c;比如oem分区。 1、创建一个1MB大小的oem.img&#xff0c;写入内容“test oem partition” 创建方式&#xff1a; dd if/dev/null …

【Linux】部署单机项目(自动化启动)---(图文并茂详细讲解)

目录 一 准备工作 1.1 连接服务器拷贝文件 1.2 解压 二 JDK安装 2.1 配置坏境变量 2.2 查看版本 三 Tomcat(自启动) 3.1 复制启动命令的位置 3.2 添加命令相关配置文件 3.2.1 配置jdk及tomcat目录 3.2.2 添加优先级 3.3 设置自启动命令 3.4 开放端口 四 My…

浅析Linux设备驱动:DMA内存映射

文章目录 概述DMA与Cache一致性DMA映射类型一致性DMA映射dma_alloc_coherent 流式DMA映射dma_map_single数据同步操作dma_direct_sync_single_for_cpudma_direct_sync_single_for_device 相关参考 概述 现代计算机系统中&#xff0c;CPU访问内存需要经过Cache&#xff0c;但外…

16. BI - 推荐系统之 ALS 实现

本文为 「茶桁的 AI 秘籍 - BI 篇 第 16 篇」 文章目录 对 MovieLens 进行电影推荐 Hi,你好。我是茶桁。 前面两节课的内容中&#xff0c;我们从矩阵分解到 ALS 原理&#xff0c;依次给大家讲解了推荐系统中的一个核心概念。 矩阵分解中拆矩阵的背后其实是聚类。就说 k 等于几…

IT廉连看——C语言——操作符

IT廉连看—操作符 c语言中有许多操作符&#xff0c;可以用于对变量进行各种不同的操作 一、算术操作符 - * / % 除了 % 操作符之外&#xff0c;其他的几个操作符可以作用于整数和浮点数。 对于 / 操作符如果两个操作数都为整数&#xff0c;执行整数除法。而只要有浮点…

tinymce问题处理

Vite构建工具下Tinymce踩坑指南 解决方案是在路劲前面增加/&#xff0c;这个跟上面链接有些区别&#xff0c;区别原因应该是如果路由采用的是createWebHashHistory则应该去掉/&#xff0c;如果是createWebHistory则应该加上/ 页面引用,一种异步加载&#xff0c;一种同步加载&…

供应链大数据:穿越经济迷雾的指南针

随着经济形势的变幻莫测&#xff0c;企业运营面临着前所未有的挑战。在这个充满不确定性的时代&#xff0c;供应链大数据如同一盏明亮的指南针&#xff0c;为企业提供精准的方向指引。下面&#xff0c;我们将深入探讨供应链大数据如何帮助企业洞察市场趋势、优化库存管理、降低…

猫毛过敏却想养猫时?如何缓解猫毛过敏?宠物空气净化器推荐

作为一个新养猫的主人&#xff0c;一开始并没有发现对猫咪过敏。直到养了半年才意识到这个问题&#xff0c;而此时我已经和猫咪有了深厚的感情。我不想放弃我的猫咪&#xff0c;但是留着它的话&#xff0c;我经常会因为流眼泪、打喷嚏、眼睛发红等过敏症状而影响日常生活&#…

神经网络系列---归一化

文章目录 归一化批量归一化预测阶段 测试阶段γ和β&#xff08;注意&#xff09;举例 层归一化前向传播反向传播 归一化 批量归一化 &#xff08;Batch Normalization&#xff09;在训练过程中的数学公式可以概括如下&#xff1a; 给定一个小批量数据 B { x 1 , x 2 , … …

WPF真入门教程29--MVVM常用框架之MvvmLight

1、MVVM模式回顾 关于mvvm模式的基础知识&#xff0c;请看这2个文章&#xff1a; WPF真入门教程23--MVVM简单介绍 WPF真入门教程24--MVVM模式Command命令 做过VUE开发或微信小程序开发的伙伴&#xff0c;就知道MVVM模式&#xff0c;核心就是数据驱动控件&#xff0c;全栈开…

软考 系统分析师系列知识点之需求获取(1)

所属章节&#xff1a; 第11章. 软件需求工程 第2节. 需求获取 需求获取是一个确定和理解不同的项目干系人的需求和约束的过程。需求获取是一件看上去很简单、做起来却很难的事情。需求获取是否科学、准备是否充分&#xff0c;对获取出来的结果影响很大&#xff0c;这是因为大部…

弱引用与C++智能指针

笔试题遇到了弱引用&#xff0c;但是C标准库是没有这个概念的&#xff0c;学了智能指针但是没有听说过弱引用&#xff0c;因此总结一下两者 学习视频链接来自B站 https://www.bilibili.com/video/BV1gV4y1G7fH?p2&vd_sourcefa4ef8f26ae084f9b5f70a5f87e9e41b智能指针 C的…

C语言:指针的进阶讲解

目录 1. 二级指针 1.1 二级指针是什么&#xff1f; 1.2 二级指针的作用 2. 一维数组和二维数组的本质 3. 指针数组 4. 数组指针 5. 函数指针 6. typedef的使用 7. 函数指针数组 7.1 转移表 1. 二级指针 如果了解了一级指针&#xff0c;那二级指针也是可以很好的理解…

基于django的购物商城系统

摘要 本文介绍了基于Django框架开发的购物商城系统。随着电子商务的兴起&#xff0c;购物商城系统成为了许多企业和个人创业者的首选。Django作为一个高效、稳定且易于扩展的Python web框架&#xff0c;为开发者提供了便捷的开发环境和丰富的功能模块&#xff0c;使得开发购物商…

第三百六十五回

文章目录 1. 概念介绍2. 方法与信息2.1 获取方法2.2 详细信息 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取设备信息"相关的内容&#xff0c;本章回中将介绍如何获取App自身的信息.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本…

【mysql】1000w数据量的分页查询SQL,如何优化提升性能?

文章目录 优化场景特别注意&#xff01;&#xff01;&#xff01;有前提&#xff0c;谨慎使用 优化场景 当表数据量非常大时&#xff0c;需要进行分页查询如果慢的时候&#xff0c;可以考虑优化下。 假设一页展示10条&#xff0c;查询第10w条后面的数据时候变慢了… 优化思路&…