C++之类和对象:构造函数,析构函数,拷贝构造,赋值运算符重载

前提:如果一个类是空类,C++中空类中真的什么都没有吗,不是的,编译器会自动生成6个默认成员函数。默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

默认成员函数:构造函数,析构函数,拷贝构造函数,赋值运算符重载,取地址重载,const取地址重载。

构造函数(完成初始化):

我们在设计类的时候,在C语言中必须有一个初始化函数才好定义变量。在C++中类如果我们没有写这个初始化函数,那么编译器就会默认生成一个默认构造函数。这个默认构造函数对内置类型不处理,自定义类型调用它的默认构造函数。

构造函数的调用:

class Data
{
public:Data(int year,int month,int day){_year = year;_month = month;_day = day;}
private://内置类型int _year = 2025;int _month = 4;int _day = 28;//自定义类型//Time _t;
};int main()
{Data day(2025,4,28);//调用带参的构造函数return 0;
}

如果是无参的构造函数:Data day;  即可,不要括号。 

构造函数的特性:

1.函数名与类名相同

2.无返回值

3.对象(也可以称为自定义类型变量)实例化时编译器自动调用对应的构造函数。

4.构造函数也可以重载。

5.如果类中没有显式定义构造函数,编译器才会生成,一旦显式定义就不会生成

6.编译器生成的默认构造函数对内置类型不处理,自定义类型调用它的默认构造函数

7.无参的构造函数,全缺省的构造函数,编译器自动生成的构造函数都是默认构造函数。

注意提醒:编译器生成的默认构造函数对于自定义类型会自动调用它的默认构造函数,而如果我们自己写了一个无参的构造函数就按照自己写的内容来,不会自动调用。

还有一个容易混淆的点:编译器自动生成的默认构造函数会自动调用自定义类型的默认构造函数,如果这个自定义类型的构造函数如果不是默认构造函数就会报错。

//会报错的代码:
class Time
{
public://不是默认构造函数Time(int hour,int minute,int second){cout << "Time" << endl;_hour = hour;_minute = minute;_second = second;}
private:int _hour;int _minute;int _second;
};class Data
{
private://内置类型int _year;int _month;int _day;//自定义类型Time _t;
};int main()
{Data day;return 0;
}

上述代码 Data 类中就有 Time 自定义类型,但是构造函数不是默认构造函数所以报错了。

//正确的代码:
class Time
{
public://全缺省函数Time(int hour=0,int minute=0,int second=0){cout << "Time" << endl;_hour = hour;_minute = minute;_second = second;}
private:int _hour;int _minute;int _second;
};class Data
{
private://内置类型int _year;int _month;int _day;//自定义类型Time _t;
};int main()
{Data day;return 0;
}

所以我们尽量使用全缺省构造函数。

还有对于编译器自动生成的默认构造函数,针对内置类型不做处理,C++11有了内置类型成员在类中声明时可以给默认值 就是在声明时给缺省值:

析构函数(完成清理工作):

析构函数是在对象销毁时完成对象中资源的清理工作,对象在销毁时会自动调用析构函数。

析构函数不是销毁对象本身,对象本身是由编译器销毁的。

析构函数的特性:

1.析构函数名是类名前面加字符~。

2.无参数也无返回值。

3.一个类只有一个析构函数,如果没有显式定义,编译器会自动生成默认的析构函数,注意:析构函数不能重载。

4.对象生命周期结束时,系统会自动调用析构函数

5.编译器生成的默认析构函数,对内置类型不做处理,对自定义类型调用它的析构函数。

6.如果类中有申请资源,例如:动态申请空间之类的需要手动销毁,必须要写析构函数手动销毁,否则会造成资源泄漏。如果没有资源申请则可以默认析构函数。

#include<iostream>
using namespace std;class Time
{
public:Time(int hour=0,int minute=0,int second=0){cout << "Time" << endl;_hour = hour;_minute = minute;_second = second;}~Time(){cout << "Time析构函数" << endl;}
private:int _hour;int _minute;int _second;
};class Data
{
public:Data(int year,int month,int day){cout << "Data" << endl;_year = year;_month = month;_day = day;}~Data(){cout << "Data析构函数" << endl;}
private:int _year = 2025;int _month = 4;int _day = 28;
};int main()
{Data day(2025,4,28);Time TY;return 0;
}

 上述代码让我们明白系统调用析构函数的顺序:局部对象(后定义先析构)-> 局部静态对象 ->

全局对象(后定义先析构):

拷贝构造函数(同类对象初始化创建对象):

拷贝构造函数:只有单个形参,形参是本类类型对象的引用(一般用const修饰)用已存在的自定义类型对象创建新对象时由编译器自动调用。

拷贝构造的特性:

1.拷贝构造函数是构造函数的一个重载形式。

2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器会报错,会引发无尽递归调用。

3.如果类中没有显式定义拷贝构造,编译器会生成默认拷贝构造函数,默认拷贝构造对内置类型按内存存储按字节序完成拷贝,是值拷贝,或者叫浅拷贝。自定义类型调用它的拷贝构造。

4.编译器的默认拷贝构造,只会完成浅拷贝,一旦类中有资源申请,拷贝构造函数必须要写,例如:有动态内存管理,那么指向动态内存的指针,浅拷贝只会拷贝地址,不会生成相同的动态内存,会导致很多问题。

5.拷贝构造典型调用场景:使用已存在对象创建新对象 / 函数参数类型为类类型对象 / 函数返回值类型为类类型对象。

class Data
{
public:Data(int year = 2025,int month = 4,int day = 29){cout << "Data" << endl;_year = year;_month = month;_day = day;}Data(const Data& d){cout << "Data拷贝构造" << endl;_year = d._year;_month = d._month;_day = d._day;}~Data(){cout << "Data析构函数" << endl;}
private://内置类型int _year;int _month;int _day;
};int main()
{Data day1;Data day2(day1);return 0;
}

 

当函数传值传参时或者返回值时调用拷贝构造:

class Data
{
public:Data(int year = 2025,int month = 4,int day = 29){cout << "Data" << endl;_year = year;_month = month;_day = day;}Data(const Data& d){cout << "Data拷贝构造" << endl;_year = d._year;_month = d._month;_day = d._day;}~Data(){cout << "Data析构函数" << endl;}
private://内置类型int _year;int _month;int _day;
};Data Test(Data d)
{Data tmp(d);return tmp;
}int main()
{Data day1;Test(day1);return 0;
}

 

实际过程中编译器为了效率会减少一些拷贝构造优化拷贝构造。为了提高效率,一般对象传参尽量使用引用类型,返回值看情况。

赋值运算符重载(把一个对象赋值给另一个对象):

运算符重载:

C++引入了运算符重载,运算符重载是具有特殊函数名的函数。

注意:1.不能通过连接其他符号来创建新操作符:operator@

2.重载操作符必须有一个类类型参数,不能去重载运算符改变内置类型的行为。

3.  .*   ::   sizeof  ?:  .   以上五个运算符不能被重载。

class Data
{
public:Data(int year = 2025,int month = 4,int day = 29){cout << "Data" << endl;_year = year;_month = month;_day = day;}Data(const Data& d){cout << "Data拷贝构造" << endl;_year = d._year;_month = d._month;_day = d._day;}//这里左操作数是this指针,指向调用函数的对象bool operator==(const Data& d2){return _year == d2._year && _month == d2._month && _day == d2._day;}~Data(){cout << "Data析构函数" << endl;}
private://内置类型int _year;int _month;int _day;
};int main()
{Data day1;Data day2(day1);cout << (day1 == day2) << endl;return 0;
}

 赋值运算符重载:

1.赋值运算符重载格式:1参数类型 const T& ,传递引用提高效率。2返回值类型 T& 支持连续赋值且提高效率。3检查是否给自己赋值。4返回 *this,要符合连续赋值的含义。

2.赋值运算符只能重载成类的成员函数不能重载成全局函数。因为类中没有显式实现,编译器会生成默认赋值运算符重载,如果在全局中也有一个,就冲突了。

3.编译器生成的默认运算符重载,对内置类型浅拷贝,对自定义类型调用它的赋值运算符重载。

还是需要注意类中是否有资源申请,有的话必须要实现。

class Data
{
public:Data(int year = 2025,int month = 4,int day = 29){cout << "Data" << endl;_year = year;_month = month;_day = day;}Data(const Data& d){cout << "Data拷贝构造" << endl;_year = d._year;_month = d._month;_day = d._day;}Data& operator= (const Data& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}bool operator== (const Data& d2){return _year == d2._year && _month == d2._month && _day == d2._day;}~Data(){cout << "Data析构函数" << endl;}
private://内置类型int _year;int _month;int _day;
};int main()
{Data day1(1,2,3);Data day2;day2 = day1;return 0;
}

和拷贝构造的区别:拷贝构造是一个存在的对象创建一个新对象并且赋值,赋值运算符重载是两个存在的对象,一个给另一个赋值。

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

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

相关文章

【专题五】位运算(1):常见位运算操作总结

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;按专题划分每题主要记录&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代码&#xff1b;&#xff08;2&#xff09;优质解法 优质代码&#xff1b;&#xff…

小草GrassRouter多卡聚合路由器聚合卫星、MESH网络应用解决方案

一、多网融合解决方案 卫星网络融合‌ 支持接入卫星通信模块&#xff0c;在无地面网络覆盖的极端场景&#xff08;如偏远山区、海洋救援&#xff09;下&#xff0c;形成“5G卫星”双链路冗余传输&#xff0c;卫星链路可作为核心通信备份&#xff0c;确保关键指令和视频数据实…

【Mybatis】Mybatis基础

文章目录 前言一、搭建MyBatis1.1 创建maven工程1.2 加入log4j日志功能1.3 MyBatis的增删改查1.4 核心配置文件详解 二、MyBatis获取参数值的两种方式2.1 单个字面量类型的参数2.2 多个字面量类型的参数2.3 map集合类型的参数2.4 实体类类型的参数2.5 使用Param标识参数 三、 M…

AI四大边界

大模型训练的边界并非由单一因素决定&#xff0c;而是技术、伦理、法律及实际应用需求共同作用的结果。以下从四个维度解析其边界来源&#xff1a; 一、技术边界&#xff1a;资源与能力的双重限制 计算资源瓶颈 成本与算力&#xff1a;大模型训练依赖海量GPU/TPU资源&#xff…

Twitter 工作原理|架构解析|社交APP逻辑

这是对Twitter 工作原理&#xff5c;架构解析&#xff5c;社交APP逻辑_哔哩哔哩_bilibili的学习&#xff0c;感谢up小凡生一 在两年半前&#xff0c;埃隆马斯克收购了Twitter&#xff0c;并且进行了一系列重大改革。今天我们来解析一下这个全球知名社交平台的架构。首先&#x…

Java基础学习内容大纲

Java基础学习内容大纲 第一阶段:建立编程思想 ​ Java概述:如何快速学习Java技术、Java历史、Java特点、Sublime、Java运行机制、JDK、转义字符、Java开发规范、Java API ​ 变量:数据类型、变量基本使用、数据类型转换 ​ 运算符:运算符介绍、算数运算符、关系运算符、…

如何对多维样本进行KS检验

对于形状为 ( 10000 , 1 , 304 ) (10000, 1, 304) (10000,1,304)的三维数据&#xff0c;若需使用scipy.stats.ks_2samp进行KS检验&#xff0c;可按以下步骤处理&#xff1a; 数据降维 KS检验要求输入为一维数组&#xff0c;需将三维数据展平或按特定维度聚合&#xff1a; • 方…

在 VMware 虚拟机中安装 Windows7

文章目录 前言1.安装VMware 虚拟机1. VMware虚拟机软件安装2. 虚拟机创建配置&#xff08;超详细步骤&#xff09;3. Windows7系统安装 3、安装 VMware tools4. VMware Tools安装与优化5. 总结与常见问题 前言 最近有不少朋友在问如何在电脑上同时使用多个操作系统&#xff0c…

直播预告|TinyVue 组件库高级用法:定制你的企业级UI体系

TinyVue 是一个跨端跨框架的企业级 UI 组件库&#xff0c;基于 renderless 无渲染组件设计架构&#xff0c;实现了一套代码同时支持 Vue2 和 Vue3&#xff0c;支持 PC 和移动端&#xff0c;包含 100 多个功能丰富的精美组件&#xff0c;可帮助开发者高效开发 Web 应用。 4 月 …

分治而不割裂—分治协同式敏捷工作模式

分治而不割裂&#xff1a;解密敏捷协同工作模式如何驱动大企业持续领跑 在数字化浪潮中&#xff0c;亚马逊仅用11天完成Prime Day全球技术架构升级&#xff0c;华为5G基站项目组创造过单周迭代47个功能模块的纪录&#xff0c;这些商业奇迹的背后&#xff0c;都隐藏着一个共性秘…

Python列表全面解析:从基础到高阶操作

一、为什么需要列表&#xff1f; 在Python中&#xff0c;列表是可变有序序列&#xff0c;用于存储多个元素的容器。相较于单一变量存储独立值&#xff0c;列表能更高效地管理批量数据&#xff0c;其特点包括&#xff1a; ​引用存储&#xff1a;列表元素存储的是对象的引用​…

Spring知识点梳理

一、Spring&#xff08;Spring Framework&#xff09; 1、IOC&#xff08;控制反转&#xff09; 1&#xff09;什么是IOC控制反转&#xff1f; 为了解藕&#xff0c;有反转就有“正转”&#xff0c;“正转”就是程序员手动 new对象&#xff1b;“反转”就是将对象的创建、对…

SpringBoot启动后自动执行方法的各种方式-笔记

1. SpringBoot启动后自动执行方法的各种方式 1.1 PostConstruct 注解 作用&#xff1a;在依赖注入完成后执行初始化方法。 适用场景&#xff1a;需要在Bean初始化时执行某些操作&#xff08;如配置、预加载数据&#xff09;。 注意&#xff1a;该方法在Bean初始化阶段执行&…

基础知识-java流steam

Java Stream 流详解 一、Stream 概述 #mermaid-svg-ZXmu5UZgAcGGq8EN {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ZXmu5UZgAcGGq8EN .error-icon{fill:#552222;}#mermaid-svg-ZXmu5UZgAcGGq8EN .error-text{fil…

8.Android(通过Manifest配置文件传递数据(meta-data))

配置文件 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"><applicationandroid:allowBackup"tr…

java 解析入参里的cron表达式,修改周时间

文章目录 前言一、java 解析入参里的cron表达式,修改周时间二、使用步骤1.示例 总结 前言 一、java 解析入参里的cron表达式,修改周时间 示例&#xff1a; 第一种: 0 0 0,16 ? * 0,1 第2种 0 0 0,16 ? * 1-7 第3种 0 0 0,16 ? * ? 第4种 0 0 0,16 ? * * 二、使用步骤 1…

DTO,VO,PO,Entity

1. DTO (Data Transfer Object) 定义 DTO 是数据传输对象&#xff0c;用于在不同系统或层之间传输数据。 目的 简化数据传输&#xff0c;降低耦合&#xff0c;通常只包含需要传输的字段&#xff0c;避免暴露内部实现细节。 使用场景 Controller 和 Service 或 远程调用 之…

从零搭建高可用分布式限流组件:设计模式与Redis令牌桶实践

一、需求背景与设计目标 在分布式系统中&#xff0c;面对突发流量时需要一种精准可控的流量控制手段。我们的组件需要具备&#xff1a; 多维度限流&#xff08;用户/IP/服务节点/自定义表达式&#xff09;分布式环境下精准控制开箱即用的Spring Boot Starter集成高扩展性的架…

Node.js 事件循环和线程池任务完整指南​

在 Node.js 的运行体系中&#xff0c;事件循环和线程池是保障其高效异步处理能力的核心组件。事件循环负责调度各类异步任务的执行顺序&#xff0c;而线程池则承担着处理 CPU 密集型及部分特定 I/O 任务的工作。接下来&#xff0c;我们将结合图示&#xff0c;详细剖析两者的工作…

echarts自定义图表--仪表盘

基于仪表盘类型的自定义表盘 上图为3层结构组成 正常一个仪表盘配置要在外圈和内圈之间制造一条缝隙间隔 再创建一个仪表盘配置 背景透明 进度条拉满 进度条颜色和数据的背景相同开始处的线 又一个仪表盘配置 数值固定一个比较小的值 <!DOCTYPE html> <html><h…