C++类与对象(下)【详析】
类与对象(下)
目录
- 类与对象(下)
- 一、再谈构造函数
- 1.构造函数体赋值
- 2.初始化列表
- 定义:
- 注意点:
- 总结:
- 3.explicit关键字
- 引入:
- explicit:
- 二、 static成员
- 回顾:static作用
- 引入:面试题
- 特征:
- 三、友元
- 四、内部类(了解)
- 五、匿名对象
- 引入:
- 作用:
- 六、拷贝对象时的一些编译器优化
- 再次理解面向对象
一、再谈构造函数
1.构造函数体赋值
在“类与对象(中)”,我们了解到在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值,如下:
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化, 构造函数体中的语句只能将其称为赋初值,而不能称作初始化;因为初始化只能初始化一次,而构造函数体内可以多次赋值
因此在次我们引入了初始化列表
2.初始化列表
定义:
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个**“成员变量”**后面跟 一个放在括号中的初始值或表达式。
特点:
①每个成员变量都要走初始化列表,就算不显示写初始化列表,也会走初始化列表;
②如果不显示写初始化列表,声明处给了缺省值就会使用缺省值初始化,如果显示写了初始化列表,即使给了缺省值也不会使用
步骤如下:
1.对于内置类型,优先使用初始化列表;没显示写初始化列表,有缺省值用缺省值,没有就用随机值;
2.自定义类型,调用它的默认构造函数,如果没有默认构造就报错。
那么,对于我们熟知的Date类,其初始化列表如下:
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成员变量
- 自定义类型成员(且该类没有默认构造函数时)
其实对于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
};
3.尽量使用初始化列表,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化;
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、尽量使用初始化列表初始化;
2、一个类尽量提供默认构造(推荐提供全缺省);
3.explicit关键字
引入:
构造函数不仅可以构造与初始化对象, 对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用
// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用Date(int year):_year(year){}
// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转换作用Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}
void Test()
{//隐式类型的转换int i=0;double d=i;const double& rd=i;//单参数构造函数Date d1(2022);Date d2=2022;const Date& d5=2022;//拷贝构造Date d3(d1);Date d4=d1;
}
这里发生了隐式类型的转换:从int类转换为了Date类,会涉及到中间开辟一个临时对象/变量,再会用这个临时对象来拷贝构造它(通常编译器优化后即为直接构造了)
若不想发生类型转换,即可使用explicit关键字
explicit:
用explicit修饰构造函数,将会禁止构造函数的隐式转换:

若是多参数构造,C++11支持,即:
Date d1={2023,3,1};
//等价于:
Date d2(2023,3,1);
注意,这里的等价于只是说明它们的功能结果相同,而实际意义却不同:第一种方式即为多参隐式构造,而第二种方式是直接构造
二、 static成员
回顾:static作用
1、在函数体内,一个被声明为静态的变量在这一函数被调用的过程中维持其值不变(维持着上次函数调用后的值,而不是每次函数调用后,值都重新进行声明)
2、在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所有函数访问,但不能被模块外其他函数访问。它是一个本地的全局变量
3、在模块内,一个被声明为静态的函数只可被这一模块内的其他函数调用。也就是,这个函数被限制在声明它的模块的本地范围内使用
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
引入:面试题
【面试题】实现一个类计算程序中创建出了多少个类对象
class A
{
public://构造函数A(){++_count;}//拷贝构造A(A& _a){++_count;}//析构函数~A(){++_count;}//定义静态变量_countstatic int GetCount(){return _count;}
private:static int _count;
};
//在类外声明静态成员变量,并给予初始化
int A::_count = 0; //静态成员变量定义初始化void test()
{cout << A::GetCount() << endl;A a1;A a2;A a3;cout << A::GetCount() << endl;
}
首先,这里可以用全局变量来实现我们的目标要求:
那为何我们要把它设为static呢?原因是全局变量容易被更改,不安全!
特征:
1.静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
2.静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
3.类静态成员即可用 类名**::静态成员 或者 对象.**静态成员来访问
4.静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5.静态成员也是类的成员,受public、 protected、 private 访问限定符的限制
因此由于static具有如上特性,它的定义方法如下:

可以注意到,首先在类外定义,不加static关键字;在类中声明变量,由于它由private限定,因此我们再提供它的Get方法;而在使用时:

我们用get方法得到它的值,再用类名::静态成员的方式对它进行访问,用于输出它的值
【问题一】静态成员函数可以调用非静态成员函数吗?
静态成员函数没有隐含的this指针,不能访问任何非静态成员,并且静态成员函数属于类而不是某个对象。
【问题二】非静态成员函数可以调用类的静态成员函数吗?
非静态成员函数可以调用类的静态成员函数。
三、友元
友元提供了一种突破封装的方式,有时提供了便利;但是友元会增加耦合度,破坏了封装,所以友元不宜多用
友元分为: 友元函数和友元类
例如,在“面向对象(中)”我们写到的流插入和流提取就用到了友元函数:
//友元声明(类中的任意位置)friend inline ostream& operator<<(ostream& out, const Date& d);friend inline istream& operator>>(istream& in, Date& d);
// 流插入的重载
inline ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}// 流提取的重载
inline istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
说明:
友元函数:
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明, 不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同
友元类:
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员
友元关系是单向的,不具有交换性;比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接 访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递:如果C是B的友元, B是A的友元,则不能说明C时A的友元
友元关系不能继承,在继承位置再给大家详细介绍
四、内部类(了解)
所谓内部类,即是类中嵌套了类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元
例如:
class A
{
private:static int k;int h;
public:class B // B天生就是A的友元{public:void func(const A& a){cout << k << endl; //OKcout << a.h << endl; //OK}};
};
int A::k = 1; //B天生是A的友元体现
int main()
{cout << sizeof(A) << endl;A aa;A::B bb; //指定域才能访问
}

五、匿名对象
引入:
我们构造一个类的对象,通常有多种方式:
对于以下类:
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
class Solution {
public:int Sum_Solution(int n) {//...return n;}
};
定义对象有以下方式:
int main()
{A aa1; //有名对象A(); //匿名对象A aa2(2); //有名对象A aa3 = 2; //有名对象return 0;
}
注意:不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义;
A aa1();
但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数:

作用:
那么匿名对象有什么作用呢?
// 匿名对象在这样场景下就很好用,当然还有一些其他使用场景;
Solution().Sum_Solution(10);
正常应该是:
Solution so;
so.Sum_Solution(10);
这里我们采用创建匿名对象调用函数
六、拷贝对象时的一些编译器优化
在传值和传返回值的过程中,一般C++编译器会做一些优化,减少对象的拷贝,这个在一些场景下是非常有用的
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;
}
【场景一】
一个表达式中,连续构造+拷贝构造,编译器会优化为一个构造
【场景二】
一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
【场景三】
一个表达式中,连续拷贝构造+赋值重载->无法优化。
再次理解面向对象
对现实世界的映射!——描述世界

相关文章:
C++类与对象(下)【详析】
类与对象(下) 目录类与对象(下)一、再谈构造函数1.构造函数体赋值2.初始化列表定义:注意点:总结:3.explicit关键字引入:explicit:二、 static成员回顾:static…...
exe反编译为.py文件
介绍公司以前的一个exe包,我们需要查看里面python源码,但是以前的py源码文件找不到,所以只能反编译,介绍一下反编译的过程。首先准备:pyinstxtractor.py这个文件,网上很多,自己下载准备查看二进…...
38 openEuler搭建FTP服务器-FTP总体介绍
文章目录38 openEuler搭建FTP服务器-FTP总体介绍38.1 FTP简介38.2 FTP使用到的端口38.3 vsftpd简介38 openEuler搭建FTP服务器-FTP总体介绍 38.1 FTP简介 FTP(File Transfer Protocol)即文件传输协议,是互联网最早的传输协议之一࿰…...
三天吃透操作系统面试八股文
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~ Github地址:https://github.com/…...
vue后台管理系统——添加i18n国际化功能——技能提升
昨天在写后台管理系统时,遇到一个需求就是需要实现国际化功能。 antd和element-ui这两个框架其实都是有国际化的。 具体展示形式就是如下: 点击右上角头部的语言,切换语言,然后整个系统的文字都改变成对应的语言展示。 切换成…...
理清gcc、g++、libc、glibc、libstdc++的关系
0 理清gcc、g++、libc、glibc、libstdc++的关系 0.1 $ dpkg -L libc6 $ dpkg -L libc6 /lib/x86_64-linux-gnu /lib/x86_64-linux-gnu/ld-2.31.so /lib/x86_64-linux-gnu/libBrokenLocale-2.31.so /lib/x86_64-linux-gnu/libSegFault.so /lib/x86_64-linux-gnu/libanl-2.31.s…...
一、快速入门 MongoDB 数据库
文章目录一、NoSQL 是什么1.1 NoSQL 简史1.2 NoSQL 的种类及其特性1.3 NoSQL 特点1.4 NoSQL 的优缺点1.5 NoSQL 与 SQL 数据库的比较二、MongoDB 基础知识2.1 MongoDB 是什么2.2 MongoDB 的体系结构2.3 MongoDB 的特点2.4 MongoDB 键特性2.5 MongoDB 的核心服务和工具2.6 Mongo…...
PMP第一章到第三章重要知识点
第1章引论 1.1指南概述和目的 PMBOK指南收录项目管理知识体系中被普遍认可为“良好实践”的那一部分: “普遍认可”:大多数时候适用于大多数项目,获得一致认可。 “良好实践”:能提高很多项目成功的可能性。 全球项目管理业界…...
【事务与锁】当Transactional遇上synchronized
事务与锁 - Transactional与Synchronize🥰前言问题回放问题一1、代码与结果复现2、原因分析3、解决方法问题二1、问题复现2、原因分析事务Transactional与锁synchronized1、synchronized与Transactional区别2、可能带来的问题3、针对问题二的解决前言 最近工作中遇…...
Pytorch模型转TensorRT步骤
Pytorch模型转TensorRT步骤 yolov5转TRT 流程 当前项目基于yolov5-6.0版本,如果使用其他版本代码请参考 https://github.com/wang-xinyu/tensorrtx/tree/master/yolov5 获取转换项目: git clone https://github.com/wang-xinyu/tensorrtx.git git …...
产品经理入门——必备技能之【产品运营】
文章目录一、基础介绍1.1 用户生命周期 & 产品生命周期1.2 运营的目的1.3 运营的阶段1.4 运营的主要工作(海盗模型)二、AARRR模型2.1 Acquisition 拉新2.2 Activision 促活2.3 Retention 留存2.4 Revenue 转化2.5 Referral 传播总结产品运营技能是产…...
【Java实现文件上传】java后端+vue前端实现文件上传全过程详解(附源码)
【写在前面】其实这篇文章我早就想写了,只是一直被需求开发耽搁,这不晚上刚好下班后有点时间,记录一下。需求是excel表格的上传,这个是很多业务系统不可或缺的功能点,再此也希望您能够读完我这篇文章对文件上传不再困惑…...
什么是SSD?SSD简述
什么是SSD?SSD简述前言一. SSD组成二. SSD存储介质存储介质按材料不同可分为三大类:光学存储介质、半导体存储介质和磁性存储介质三. SSD接口形态固态硬盘有SATA 3.0接口、MSATA接口、M.2接口、PCI-E接口、U.2接口五种类型。三. SSD闪存颗粒分类闪存颗粒…...
MySQL基础------sql指令1.0(查询操作->select)
目录 前言: 单表查询 1.查询当前所在数据库 2.查询整个表数据 3.查询某字段 4.条件查询 5.单行处理函数(聚合函数) 6.查询时给字段取别名 7.模糊查询 8.查询结果去除重复项 9.排序(升序和降序) 10. 分组查询 1…...
Python数据分析处理报告--实训小案例
目录 1、实验一 1.1、题目总览 1.2、代码解析 2、实现二 2.1、题目总览 2.2、代码解析 3、实验三 3.1、题目总览 3.2、代码解析 4、实验四 3.1、题目总览 3.2、代码解析 哈喽~今天学习记录的是数据分析实训小案例。 就用这个案例来好好巩固一下 python 数据分析三…...
OpenCV入门(十二)快速学会OpenCV 11几何变换
OpenCV入门(十二)快速学会OpenCV 11几何变换1.图像平移2.图像旋转3.仿射变换4.图像缩放我们在处理图像时,往往会遇到需要对图像进行几何变换的问题。图像的几何变换是图像处理和图像分析的基础内容之一,不仅提供了产生某些图像的可…...
小菜鸟Python历险记:(第二集)
今天写的文章是记录我从零开始学习Python的全过程。Python基础语法学习:Python中的数值运算一共有7种,分别是加法()、减法(-)、除法(/)得到的结果是一个浮点数、乘法(*&a…...
ContentProvider程序之间数据的相互调用
1权限的获取和调用 权限分为普通权限和危险权限,除了日历信息,电话,通话记录,相机,通讯录,定位,麦克风,电话,传感器,界面识别(Activity-Recognit…...
金三银四最近一次面试,被阿里P8测开虐惨了...
都说金三银四涨薪季,我是着急忙慌的准备简历——5年软件测试经验,可独立测试大型产品项目,熟悉项目测试流程...薪资要求?5年测试经验起码能要个20K吧 我加班肝了一页半简历,投出去一周,面试电话倒是不少&a…...
算法题——给定一个字符串 s ,请你找出其中不含有重复字符的最长子串 的长度
给定一个字符串 s ,请你找出其中不含有重复字符的最长子串 的长度 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “b”&am…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
