当前位置: 首页 > news >正文

C++类和对象(下部曲)

构造函数
 

1 构造函数体赋值


在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值
虽然对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化

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

2 初始化列表
 

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

初始化列表是每个成员定义的地方

注意:
1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化
    引用成员变量:因为引用必须初始化,那就需要在定义时初始化
    const成员变量:const成员不能被修改,在定义的时候就要给值,否则就不能给值了
    自定义类型成员(且该类没有默认构造函数时):

尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,
一定会先使用初始化列表初始化
4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后
次序无关
 

3 explicit关键字
 

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

1. 单参构造函数没有使用explicit修饰,具有类型转换作用

    explicit修饰构造函数,禁止类型转换
2. 虽然有多个参数,但是创建对象时只有第一个参数没有默认值,那么若没有使用explicit修饰,       具有类型转换作用

class Date
{
public: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 d(2);d = 2023;//发生类型转换return 0;
}

用整型2023去赋值给日期类对象,实际上会用2023去构造一个临时对象,再用这个临时对象去赋值日期类对象

但若用explicit修饰构造函数后,禁止了类型转换,会报错

static成员
 

1 概念
 

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

2 特性
 

1. 静态成员所有类对象所共享不属于某个具体的对象,存放在静态区
2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

累积创建了多少个对象:调用构造函数的次数+调用拷贝构造函数的次数

正在使用的还有多少个对象:调用构造函数的次数+调用拷贝构造函数的次数-调用析构函数的次数

对象创建会自动调用构造函数,若是对象是用已存在对象初始化地创建,那就会自动调用拷贝构造函数,对象生命周期结束了会自动调用析构函数,所以以上两种问题可以通过统计构造函数、拷贝构造函数、析构函数的调用次数轻松解决

class A
{
public:A(){n++;m++;}A(const A& a){n++;m++;}~A(){m--;}static int GetN()//没有了this指针,在类外可以用类名::函数名直接调用,不用通过对象.函数名调用{return n;}static int GetM()//没有了this指针{return m;}
private:static int n;//累计创建的对象  静态成员变量属于所有A对象,属于整个类static int m;//正在使用的对象
};int A::n = 0;//在类外定义
int A::m = 0;int main()
{A a1;A a2;A a3(a1);A();//匿名对象,生命周期只在这一行cout << A::GetN() <<" "<<A::GetM()<< endl;cout << a1.GetN() << " " << a1.GetM() << endl;return 0;
}

友元
 

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

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

1 友元函数
 

在实际中若要重载operator<<,但是我们会发现没办法将operator<<重载成成员函数,因为ostream类型的对象要是第一个参数,但是在类的成员函数中,第一个参数是this指针,要是硬重载成为成员函数也是ok的,但是使用起来会非常别扭,比如:

class A
{
public:ostream& operator<<(ostream &out){out << _a << endl;return out;}
private:int _a=0;
};int main()
{A a;a << cout;return 0;
}

 是可以运行起来的,但是使用很别扭,所以我们要将operator<<重载成全局函数,这样两个参数的位置就可以由我们来分配了,但又会导致类外没办法访问成员,此时就需要友元来解决
 

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

class A
{friend ostream& operator<<(ostream& out, const A& a);friend istream& operator>>(istream& in, A& a);private:int _a=0;
};int main()
{A a;cin >> a;cout << a << endl;return 0;
}istream& operator>>(istream& in, A& a)
{in >> a._a;return in;
}ostream& operator<<(ostream& out,const A&a)
{out << a._a << endl;return out;
}

注意:

友元函数访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰,因为没有this指针
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同

2 友元类
 

1  友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有成员
2  友元关系是单向的,不具有交换性
 比如现在有Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类可以直接
访问Time类的私有成员变量,但不可以在Time类中访问Date类中私有成员变量
3 友元关系不能传递
 如果C是B的友元, B是A的友元,不能说明C是A的友元
4 友元关系不能继承

class Time
{friend class Date;
public:Time(int hour = 1, int minute = 1, int second = 1):_hour(hour),_minute(minute),_second(second){}
private:int _hour;int _minute;int _second;
};class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day){}void TimeSet(int hour, int minute, int second){_t._hour = hour;//直接访问Time类的私有成员_t._minute = minute;_t._second = second;}
private:int _year;int _month;int _day;Time _t;
};

3 内部类
 

如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,
不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越
的访问权限

内部类就是外部类的友元类

特性:
 

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

class A
{
public:class B//B天生就是A的友元{public:void Print(const A&a){cout << i << endl;cout << a._m << endl;}};
private:static int i;int _m = 3;
};int A::i = 4;int main()
{A::B b;b.Print(A());return 0;
}

匿名对象
 

匿名对象的生命周期只有一行

匿名对象这样定义:

class A
{
private:int _a;
};int main()
{A();//匿名对象return 0;
}

平常我们要调用类的成员函数,需要先写一行创建类对象,然后再写一行用对象去调用

class A
{
public:void Print(){cout << _a << endl;}
private:int _a = 9;
};int main()
{A a;a.Print();return 0;
}

此时匿名对象的作用就体现出来了:
 

class A
{
public:void Print(){cout << _a << endl;}
private:int _a = 9;
};int main()
{A().Print();return 0;
}

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

 在传参和传返回值的过程中,一般编译器会做一些优化,以减少对象的拷贝

以下面代码为示例:


class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << endl;}A(const A& a){_a = a._a;cout << "A(const A& a)" << endl;}A& operator=(const A& a){cout << "A& operator=(const A& a)" << endl;if (this != &a){_a = a._a;}return *this;}~A(){cout << "~A()"<<endl;}private:int _a;
};void f1(A aa)
{}A f2()
{A aa;return aa;
}

1 传值传参

int main()
{// 传值传参A aa1;//调用构造函数f1(aa1);//传值传参,调用拷贝构造(用已存在的对象去初始化地创建新对象)return 0;
}

 2 传值返回

A f2()
{A aa;//调用构造函数return aa;//aa作为局部变量出了函数作用域就销毁了,那么会生成一个临时变量(用aa去拷贝构造生成),返回的不是aa,是临时对象
}
int main()
{// 传值返回f2();return 0;
}

 

 

3 隐式类型

void f1(A aa)
{}
int main()
{// 隐式类型,连续构造+拷贝构造->优化为直接构造f1(1);return 0;
}

用整型1给A类对象aa传参,会发生隐式类型转换:

首先,会生成一个临时对象(用1去构造这个临时对象)

其次,用这个临时对象去拷贝构造aa

连续的步骤是:构造+拷贝构造

连续的构造,编译器会优化,直接合并成一步:构造

4 表达式传参

void f1(A aa)
{}
int main()
{// 一个表达式中,连续构造+拷贝构造->优化为一个构造f1(A(2));return 0;
}

 用匿名对象给A类对象aa传参:

首先,对匿名对象调用构造函数,初始化为2

其次,用匿名对象拷贝构造生成对象aa

连续的步骤:构造+拷贝构造

编译器做优化:直接合并为调用一次构造

 

 5

A f2()
{A aa;//调用构造函数return aa;//aa作为局部变量出了函数作用域就销毁了,那么会生成一个临时变量(用aa去拷贝构造生成),返回的不是aa,是临时对象
}
int main()
{// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造A aa2 = f2();return 0;
}

1 调用f2函数,首先创建对象aa,调用了构造函数

2  f2调用结束,要返回aa对象,要生成一个临时对象,用aa拷贝构造生成

3 临时对象带回返回值,临时对象拷贝构造对象aa2

连续的步骤:步骤2+3 即拷贝构造+拷贝构造

编译器优化:直接合并为调用一次拷贝构造

在aa销毁前,用aa拷贝构造aa2

A f2()
{A aa;//调用构造函数return aa;//aa作为局部变量出了函数作用域就销毁了,那么会生成一个临时变量(用aa去拷贝构造生成),返回的不是aa,是临时对象
}
int main()
{A aa1;// 一个表达式中,连续拷贝构造+赋值重载->无法优化aa1 = f2();return 0;
}

1 创建aa2,调用构造

2 调用f2函数,创建aa,调用析构

3 返回局部变量aa,先生成临时变量,aa拷贝构造临时对象

4 临时对象赋值给对象aa1

连续步骤:3+4 即拷贝构造+赋值重载

由于不属于一个派系,所以编译器无法优化

7

int main()
{A aa2 = 1;//类型转换return 0;
}

首先会用1去构造一个临时对象,再用这个临时对象去拷贝构造aa2

连续的构造+拷贝构造,编译器直接优化成:构造

 

 

nt main()
{A aa3 = A(2);return 0;
}

首先用2去构造匿名对象,然后用它去拷贝构造aa3

连续的构造+拷贝构造,编译器直接优化为:构造

然后再调用析构函数,析构aa3对象 

相关文章:

C++类和对象(下部曲)

构造函数 1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值 虽然对象中已经有了一个初始值&#xff0c;但是不能将其称为对对象中成员变量的初始化 构造函数体中的语句只能将其称为赋初值&#xff0c;而…...

解决eclipse 打开报错 An error has occurred. See the log file null.

解决eclipse 打开报错an error has ocurred. See the log file null 出现原因&#xff1a;安装了高版本的jdk,更换 jdk 版本&#xff0c;版本太高了。 解决方案&#xff1a;更改环境变量 改成 jkd 1.8...

javascript学习

一、数据类型 所有的变量都以var定义 数值 js不区分小数和整数 文本图形音频视频数组 var id_arr [1,2,3,4,5]对象 // 定义对象 var person {name: zhangsan,age: 3,tags: [java,js,php]} // 取对象的值 var person_name person.name...

基于SSM实现个人随笔分享平台:创作心灵,分享自我

项目简介 本文将对项目的功能及部分细节的实现进行介绍。个人随笔分享平台基于 SpringBoot SpringMVC MyBatis 实现。实现了用户的注册与登录、随笔主页、文章查询、个人随笔展示、个人随笔查询、写随笔、草稿箱、随笔修改、随笔删除、访问量及阅读量统计等功能。该项目登录模…...

从零开始学Docker(二):启动第一个Docker容器

宿主机环境&#xff1a;RockyLinux 9 这个章节不小心搞成命令学习了&#xff0c;后面在整理成原理吧 Docker生命周期 拉取并启动Nginx容器 # 查找镜像 例如&#xff1a;nginx [root192 ~]# docker search nginx 我们可以看到&#xff0c;第一个时官方认证构建的nginx # 拉…...

unity 鼠标事件

Input.GetMouseButtonDown(0&#xff09;点击屏幕Input.mousePosition鼠标的坐标Input.GetKeyDown(KeyCode.Space)点击空格 1.2D游戏中鼠标触发事件 using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; using UnityEditor; using Un…...

【ChatGPT】相关解读

ChatGPT 背后的“功臣”——RLHF 技术详解 Meta 发布开源可商用模型 Llama 2&#xff0c;实际体验效果如何&#xff1f; Llama 2线上试用地址&#xff1a;replicate.com/a16z-infr…...

【数据中台】DataX源码进行二开插件

参考官方 使用的离线数据同步工具/平台&#xff0c;实现不同数据库等各种异构数据源之间高效的数据同步功能 工具部署 https://github.com/alibaba/DataX/blob/master/userGuid.md 拉取下来的代码&#xff0c;pom.xml里面注释 <!--<module>tsdbreader</module&g…...

【数据结构与算法】基数排序

基数排序 基数排序&#xff08;Radix Sort&#xff09;属于“分配式排序”&#xff0c;又称“桶子法”或 bin sort&#xff0c;顾名思义&#xff0c;它是通过键值的各个位的值&#xff0c;将要排序的元素分配至某些“桶”中&#xff0c;达到排序的作用。基数排序法是属于稳定性…...

Java基础一(队列和堆栈)

//示例 //添加新的元素 stack.push(Element e)queue.add(Element e) //满报IllegalStateException异常 queue.offer(Element e) //满成功true&#xff0c;否则false //删除 stack.pop()queue.remove() //移除头部元素&#xff0c;空报异常 queue.poll() //移除头部元素&…...

使用ansible playbook编写lnmp架构

使用ansible playbook编写lnmp架构 - name: nginx playgather_facts: falsehosts: lnmpremote_user: roottasks: - name: stop firewalldservice: namefirewalld statestopped- name: syslinuxcommand: /usr/sbin/setenforce 0ignore_errors: true- name: nginx.repocopy: src/…...

使用 TorchText 进行语言翻译

使用 TorchText 进行语言翻译 本教程说明如何使用torchtext的几个便捷类来预处理包含英语和德语句子的著名数据集的数据&#xff0c;并使用它来训练序列到序列模型&#xff0c;并注意将德语句子翻译成英语 。 它基于 PyTorch 社区成员 Ben Trevett 的本教程&#xff0c;并由 …...

SpringBoot整合SSMP小demo

创建项目 spring web&#xff0c;mybatis&#xff0c;mysql勾选 加入mp和druid&#xff0c;依赖见SpringBoot基础认识_阳光明媚UPUP的博客-CSDN博客 yml数据源 server:port: 81 spring:datasource:druid: #整合方式配置driver-class-name: com.mysql.jdbc.Driverurl: jdbc:m…...

51单片机--红外遥控

文章目录 红外遥控的介绍硬件电路NEC编码外部中断红外遥控实例代码 红外遥控的介绍 红外遥控是一种无线、非接触控制技术&#xff0c;通过使用红外线来传送控制信号。它具有抗干扰能力强、信息传输可靠、功耗低、成本低、易实现等显著优点&#xff0c;因此被广泛应用于各种电子…...

【图像分类】CNN+Transformer结合系列.2

介绍几篇利用CNNTransformer实现图像分类的论文&#xff1a;CMT&#xff08;CVPR2022&#xff09;&#xff0c;MaxViT(ECCV2022)&#xff0c;MaxViT&#xff08;ECCV2022&#xff09;&#xff0c;MPViT&#xff08;CVPR2022&#xff09;。主要是说明Transformer的局限性&#x…...

用于毫米波天线的新型无卤素超低传输损耗多层电路板R-5410

3月3日消息&#xff0c;松下公司宣布&#xff0c;其工业解决方案公司已经实现了R-5410的商业化&#xff0c;这是一种无卤素、超低传输损耗的多层电路板&#xff08;MLCB&#xff09;材料&#xff0c;适用于毫米波天线。将于2021年3月开始量产。 毫米波雷达是汽车、通信等行业的…...

java数据算法-汉诺塔

1、有三根相邻的柱子&#xff0c;标号为A,B,C。 2、A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘。 3、现在把所有盘子一个一个移动到柱子C上&#xff0c;并且每次移动同一根柱子上都不能出现大盘子在小盘子上方。 题解步骤 1、当n1时&#xff1b; 将1号从A移动到C即…...

[QT编程系列-35]:数据存储 - JSON格式配置数据的存储与通知

目录 1. QJsonObject 2 QJsonDocument 3 JSON本文格式 4. JSON示例 5. JASON配置文件示例 1. QJsonObject QJsonObject 是Qt的类之一&#xff0c;用于表示 JSON 对象。 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0…...

【Spring】Spring 中事务的实现

目录 1.编程式事务&#xff08;手动编写代码&#xff09;2.声明式事务&#xff08;利用注解&#xff09;2.1 Transactional作用范围2.2 Transactional参数说明2.3 Transactional工作原理 3.Spring 中设置事务隔离级别3.1 事务四大特性ACID3.2 事务的隔离级别3.2 Spring中设置事…...

Linux 学习记录60(ARM篇)

Linux 学习记录60(ARM篇) 本文目录 Linux 学习记录60(ARM篇)一、SPI总线1. 概念2. 硬件连接 二、SPI总线协议三、SPI总线通信模式四、对比IIC总线和SPI总线1. 相同点2. 不同点 思维导图 一、SPI总线 1. 概念 1、SPI总结是Motorola首先提出的全双工三线/四线同步串行总线 2、采…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...

TJCTF 2025

还以为是天津的。这个比较容易&#xff0c;虽然绕了点弯&#xff0c;可还是把CP AK了&#xff0c;不过我会的别人也会&#xff0c;还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...