C++ 类和对象(中篇)
类的6个默认成员函数
如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情 况下,都会自动生成下面6个默认成员函数。

构造函数:
定义:构造函数是一个特殊的成员函数,名字与类名相同,创建类 类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。
特性:
1.不是开空间创建对象,而是初始化对象。
2. 函数名与类名相同。
3. 无返回值。
4. 对象实例化时编译器自动调用对应的构造函数。
5. 构造函数可以重载。
#include <iostream>
#include <assert.h>using namespace std;class Date
{
private:int _year;int _month;int _day;public://1.无参构造函数Date(){}// 2.带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}
};int main()
{Date d1; // 调用无参构造函数Date d2(2015, 1, 1); // 调用带参的构造函数// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象Date d3();return 0;
}
根据不同的初始化需求,去选择构造函数:
6.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
class Date
{
private:int _year;int _month;int _day;void MPrintf(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}
};int main()
{
// 没有定义构造函数,对象也可以创建成功,因此此处调用的是编译器生成的默认构造函数Date d1;d1.MPrintf();return 0;
}

7.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参 构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。
默认成员函数分为:

为什么默认构造函数只能有一个?
回答:
当同时有无参构造函数和全缺省构造函数时,在实例化过程中编译器无法判断选择哪一种构造函数。
//为什么默认构造函数只能有一个?
class Date
{
private:int _year;int _month;int _day;public://1.无参构造函数Date(){}// 2.全缺省构造函数Date(int year=2018, int month=1, int day=1){_year = year;_month = month;_day = day;}void MPrintf(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}
};int main()
{ // 调用无参构造函数Date d1; //全缺省构造函数--注释1.无参构造函数Date d2(1111);d2.MPrintf();Date d3(2222, 2);d3.MPrintf();Date d4(3333, 3, 3);d4.MPrintf();return 0;
}

此时编译器是无法确定选择哪一种默认构造函数;
全缺省构造函数结果:

8.默认构造函数多用于自定义类型
对于自定义类型(复杂情况)我们会用构造函数的默认生成,更方便
class Time
{
public:
//默认构造Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}
调试结果:

对于内置类型,默认构造函数时不进行内容的改变,只保留随机值。
对于自定义类型,我们设置了他的默认成员函数,随机值进行了改变,在以后多次使用tmie类型是,他的初始化内容都会改变成自己一开始设置的初始化内容。
内置和自定义混合的,可以给内置缺省

此处不是初始化(空间没有创造就不能算是初始化,只能算是声明缺省值)

解释:默认构造没有参数传递,使用其原本的缺省值
由此得出:
默认生成构造函数:
1.内置类型成员不做处理
2.自定义类型成员,会去调用它的默认构造(不用传参数的构造)
建议:每个类都提供一个默认构造函数(内置类型->缺省构造)
析构函数:
定义:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
特征:
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值。
3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

拷贝构造函数:
定义:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。(将已近实例化好的类型对象拷贝给将要实例化的新的对象的构造函数)
特征:
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。

为什么只传一个参数?
回答:其在结构体内部,本身还有一个隐形的This指针。
必须使用引用的原因?
回答:避免出现使用传值方式会引发无穷递归调用。--在拷贝时先准备启用拷贝构造函数,传值时会调用拷贝构造函数。这样会形成无穷递归调用。(不理解直接记成:拷贝构造函数必用引用)
使用const的原因?
回答:在传递过程中,将可读可写的修改成const(只读)模式。将其权限缩小,避免因为自己思路的问题而导致原本值的改变。
3. 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
代码:
#include <iostream>using namespace std;class Date
{
public://构造函数-全缺省Date(int year = 2018, int month = 1, int day = 1){_year = year;_month = month;_day = day;}拷贝构造函数//Date(const Date& d)//{// _year = d._year;// _month = d._month;// _day = d._day;//}void Mprintf(){cout << _year << "/" << _month<<"/" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Mprintf();Date d2(d1);d2.Mprintf();return 0;
}
结果:

通过两次运行结果对比发现貌似这个拷贝构造函数有无都一样?
其实不然,C语言本身是对一些内置类型(int char等)在编译器的底层是有类似拷贝构造函数的结构的。
但是,面对一些自定义类型,或者申请有申请空间的情况,C语言不能满足需求
那就需要我们自己写专门针对自己的类的类型的拷贝构造函数
如以下代码:
//开辟一个字符串空间
class String
{
public:String(const char* str = "jack"){_str = (char*)malloc(strlen(str) + 1);strcpy(_str, str);}//析构函数~String(){cout << "~String()" << endl;free(_str);}
private:char* _str;
};
int main()
{String s1("hello");String s2(s1);
}
此时拷贝的运行结果就是错误。
什么情况下用拷贝构造函数:
自己实现了析构函数释放空间,就需要实现拷贝构造函数;
拷贝构造应用场景:
获取x天后的日期:
//拷贝构造应用场景
class Date
{
public://构造函数--全缺省Date(int year = 2018, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//打印函数void Mprintf(){cout << _year << "/" << _month << "/" << _day << endl;}int GetMonthDay(int year,int month){assert(month > 0 && month < 13);//正常情况下当前月数返回的天数,月从1开始,所以多加一位占位int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//闰年2月天数情况if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0)){return 29;}else{return monthArray[month];}}//x天后的日期Date GetAfterXDay(int x){//因为不想改变初始日期所以使用拷贝Date tmp = *this;tmp._day += x;while (tmp._day > GetMonthDay(tmp._year, tmp._month)){tmp._day -= GetMonthDay(tmp._year, tmp._month);++tmp._month;if (tmp._month == 13){tmp._year++;tmp._month = 1;}}return tmp;}private:int _year;int _month;int _day;
};int main()
{Date d1(2222, 2, 2);//d1.GetAfterXDay(100)调用类d1里的GetAfterXDay函数//返回一个Date类型的对象再拷贝到(Date d2=返回值)d2中Date d2 = d1.GetAfterXDay(100);d1.Mprintf();d2.Mprintf();return 0;
}
赋值运算符重载
运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)(对于比较类,不需要更改值,常用const修饰)
用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
.* 、:: 、sizeof 、?: (三目运算符)、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
公共成员:
要在类外调用还得将声明对象的private改成public
class Date
{
public:Date(int year = 1111, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//private:int _year;int _month;int _day;
};
//d1与d2位置分别对应==的两端,不能修改
//参数和操作数(函数形参位置)是成正比的
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}
int main()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout << (d1 == d2) << endl;//d1==d2会自动call对于函数地址//改变成->operator==(d1,d2)进行比较
}
为什么要在输出是加括号:
回答:因为<< 和== 优先级不同cout << d1 == d2 << endl;只会先识别cout << d1
成员函数:
class Date
{
public:Date(int year = 1111, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
//因为是成员函数,所以有其自身的this
// 完全展开后:bool operator==(Date* this, const Date& d2)bool operator==(const Date& d2){return _year == d2._year&& _month == d2._month&& _day == d2._day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);d1 == d2;//因为是成员函数,其内部自身有隐藏this所以值传递一个值d1.operator==(d2);cout << (d1 == d2) << endl;cout << d1.operator==(d2) << endl;
}
并不局限于==除了.* 、 :: 、 sizeof 、 ?: (三目运算符) 、 . 这5个外都可以用operator(运算符)更改
运算符重载的复用:
代码:
//运算符重载的复用
class Date
{
public:Date(int year = 1111, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//因为是成员函数,所以有其自身的this// 完全展开后:bool operator==(Date* this, const Date& d2)bool operator==(const Date& d2){return _year == d2._year&& _month == d2._month&& _day == d2._day;}//复用!!//由于已知了==,根据==就能写出!=(> < >= <=(小于或等于)同理)bool operator!=(const Date& d){return !(*this == d);//相等1再!->false//不等0再!->true}
private:int _year;int _month;int _day;
};int main()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout << (d1!=d2) << endl;//显示1,truereturn 0;
}
赋值运算符重载 =
主要特点:
1. 参数类型
2. 返回值
3. 检测是否自己给自己赋值
4. 返回*this
5. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
赋值运算使用:
常用域自己实现了析构函数释放空间,就需要使用自己编写的赋值运算符重载;

class Date
{
public:Date(int year = 1111, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Mprintf(){cout << _year << "/" << _month<<"/" << _day << endl;}
private:int _year;int _month;int _day;
};class String
{
public://拷贝String(const char* str = ""){_str = (char*)malloc(strlen(str) + 1);strcpy(_str, str);}//析构~String(){cout << "~String()" << endl;free(_str);}void Mprintf(){cout << _str << endl;}
private:char* _str;
};int main()
{Date d1(2222, 9, 26);Date d2(3333, 9, 27);String s1("hello");String s2("world");d1.Mprintf();d2.Mprintf();s1.Mprintf();s2.Mprintf();//编译器生成的默认赋值重载函数已经可以完成字节序的值拷贝了d1 = d2;s1 = s2;//报错//此时,自己没有写赋值重载函数,当s2赋值给s1后//s1和s2同时指向相同的一块空间(s2开出的)//后序清理资源,清理s2的时候,s1所指向的空间也会改变//所以错误,编译器不通过d1.Mprintf();s1.Mprintf();return 0;
}
连续赋值
有返回值用于支持这里的连续赋值,保持运算符的特性
eg.i=j=k;连续赋值的顺序是先j=k后返回j再i=j;
所以由于以上连续赋值的可能性,也要考虑赋值运算符重载的连续赋值的可能。
class Date
{
public:Date(int year = 1111, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Mprintf(){cout << _year << "/" << _month<<"/" << _day << endl;}//d3=d2=d1//返回的是赋值的左操作数所以返回*this//返回后该操作数的生命周期还存在所以用引用返回Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}
private:int _year;int _month;int _day;
};int main()
{Date d1(1111, 1, 1);Date d2(2222, 2, 2);Date d3(3333, 3, 3);d1.Mprintf();//1111,1,1d1 = d2 = d3;d1.Mprintf();//3333,3,3return 0;
}
自己给自己赋值
有时不小心的操作可能会造成自己给自己赋值的情况
所以还需要在自己写的赋值情况中加入if判断

总结:
赋值重载和拷贝构造区别:
赋值重载是多个已近定义出来的对象
拷贝构造是一个已经实例化的对象初始化另一个未实例化的对象

在使用引用返回时,不能盲目的为了追求使用引用返回而使用静态变量static
静态变量static在整体函数调用中,只会初始化他的第一次,再次走到static内一步时,不会再初始化,而是使用其之前的值;
相关文章:
C++ 类和对象(中篇)
类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情 况下,都会自动生成下面6个默认成员函数。 构造函数: 定义:构造函数是一个特殊的成员…...
可视化场景(9):智慧看板,可能是最直观的数据展示
10年经验的大数据可视化和数字孪生老司机,该领域的专家,是您可信赖的技术合伙人,分享该领域的项目和作品,欢迎互动交流。 hello,我是贝格前端工场,本期分享可视化大屏在安全生产与设备运维场景的应用&#…...
加密算法(二)
1、SHA-256加密算法: package com.arithmetic.encryption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; //使用java.security.MessageDigest类来进行SHA-256摘要的计算。 //通过getInstance("SHA-256")方法获取…...
大创项目推荐 深度学习 YOLO 实现车牌识别算法
文章目录 0 前言1 课题介绍2 算法简介2.1网络架构 3 数据准备4 模型训练5 实现效果5.1 图片识别效果5.2视频识别效果 6 部分关键代码7 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于yolov5的深度学习车牌识别系统实现 该项目较…...
IP知识详解
IP基本认识 IP 在 TCP/IP 参考模型中处于第三层,也就是网络层。 网络层的主要作用是:实现主机与主机之间的通信,也叫点对点(end to end)通信。 网络层与数据链路层有什么关系呢? IP 的作用是主机之间通信…...
设计模式:适配器模式
定义 适配器模式(Adapter Pattern),也称为包装器(Wrapper)模式,是一种结构型设计模式,它允许不兼容的接口之间进行交互。适配器模式通过包装一个已有的类,提供一个与原系统兼容的接…...
大语言模型落地的关键技术:RAG
1、什么是RAG? RAG 是检索增强生成(Retrieval-Augmented Generation)的简称,是当前最火热的大语言模型应用落地的关键技术,主要用于提高语言模型的效果和准确性。它结合了两种主要的NLP方法:检索ÿ…...
ffmpeg Android 笔记
目录 没有示例项目 编译好的.a文件 ffmpegandroid 延时有220ms rk官方有例子 ffmpeg Android 笔记 没有示例项目 编译好的.a文件 FFmpeg-Android/ffmpeg-android-aarch64-34/lib at main yhbsh/FFmpeg-Android GitHub ffmpegandroid 看到了音频解码器 FFmpegAndroid/a…...
本地创建新分支并提交gitee
初始化本地仓库 git init链接远程仓库 git remote add origin https://gitee.com/xxxxxxxxxxx提交本地代码(进行commit提交) git add . git commit -m "分支名"创建分支 git branch 分支名选择刚刚创建的分支 git checkout 分支名查看所选中的分支 git branch …...
[蓝桥杯 2019 国 C] 数正方形
[蓝桥杯 2019 国 C] 数正方形 题目描述 在一个 N N N \times N NN 的点阵上,取其中 4 4 4 个点恰好组成一个正方形的 4 4 4 个顶点,一共有多少种不同的取法? 由于结果可能非常大,你只需要输出模 1 0 9 7 10^9 7 1097 的…...
Redis: 配置文件详解(Redis.conf)
文章目录 一、Units二、INCLUDES三、NETWORK四、GENERAL五、SECURITY六、LIMITS 一、Units 单位,配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit,大小写不敏感 二、INCLUDES 包含,多…...
学习vue3第十四节 Teleport 内置组件介绍
<Teleport></Teleport> 作用目的: 用于将指定的组件或者元素传送到指定的位置; 通常是自定义的全局通用弹窗,绑定到 body 上,而不是在当前元素上面; 使用方法: 接收两个参数 to: 要将目标传…...
mybatis模糊查询查不到数据
排除SQL语句本身存在错误,字段名称不匹配,编码格式问题后,若使用%方式查询,一开始使用单引号查询不到数据,把改成",可以查询到数据 疑问:看别人的代码,使用单引号也可以查询到数据,原因未知...
Python语法总结:not(常出现错误)
0、not是什么 在python中not是逻辑判断词,用于布尔型True和False之前 a not Ture # a False b not False # b True1、not的用法 (1)判断语句 if not a:# 如果a是False,执行的语句(2)判断元素是否在…...
深入理解WebSocket:实时双向通信的利器
一、介绍 1.1 基础概念介绍 单工通信:数据传输只允许在一个方向上传输,只能一方发送数据,另一方接收数据并发送。半双工:数据传输允许两个方向上的传输,但在同一时间内,只可以有一方发送或接收数据。全双…...
Gateway是什么?(SpringCloudAlibaba组件)
1、网关介绍 **网关(Gateway)又称网间连接器、协议转换器。网关在传输层上以实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连。**网关的结构也和路由器类似,不同的是互连层。网关既可以用于广域网互连,也可…...
阿里巴巴拍立淘API新功能揭秘:图片秒搜商品,实现智能化个性化购物新体验
在数字化快速发展的今天,智能化和个性化已经成为购物体验中不可或缺的元素。为了满足消费者日益增长的购物需求,阿里巴巴中国站不断推陈出新,其中拍立淘API的新功能——图片秒搜商品,无疑为智能化个性化购物体验开创了新的篇章。 …...
蚓链为移动实体经济加油!
在当今数字化时代,数据已成为企业宝贵的资产之一。如何利用数据资产为可移动实体经济创造更多的增值机会呢?蚓链将为你揭示 11种行之有效的方法! 1. 个性化服务:根据客户数据,提供量身定制的产品和服务,满…...
MySQL 核心模块揭秘 | 12 期 | 创建 savepoint
回滚操作,除了回滚整个事务,还可以部分回滚。部分回滚,需要保存点(savepoint)的协助。本文我们先看看保存点里面都有什么。 作者:操盛春,爱可生技术专家,公众号『一树一溪』作者&…...
SpringMVC --- 老杜
1、什么是SpringMVC? SpringMVC是一个基于Java实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的及部分,…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
