【C++】类中的“默认成员函数“--构造、析构、赋值

目录

概念引入:

一、构造函数

问题引入:

1)构造函数的概念

2)构造函数的特性

二、析构函数

1)析构函数概念

2)析构函数特性

三、拷贝构造函数

1)拷贝构造函数概念

示例代码:

2)深拷贝

3)拷贝构造函数特性

四、赋值运算符重载

运算符重载

赋值运算符重载

概念引入:

C++中的默认成员函数是系统自动生成的,如果没有手动编写该类的成员函数,编译器就会自动为该类生成默认成员函数。默认成员函数包括默认构造函数、默认析构函数和默认拷贝构造函数等。

  1. 默认构造函数:当创建对象时,如果没有显式地调用构造函数,系统会自动调用默认构造函数来初始化对象。默认构造函数不接受任何参数,也不返回任何值。
  2. 默认析构函数:当对象被销毁时,系统会自动调用析构函数来清理对象。默认析构函数不接受任何参数,也不返回任何值。
  3. 默认拷贝构造函数:当将一个对象赋值给另一个对象时,系统会自动调用拷贝构造函数来完成对象的复制。默认拷贝构造函数会将原对象的所有成员变量逐个复制给新对象。

除了以上三种默认成员函数外,还有默认赋值运算符、取地址运算符等。这些默认成员函数可以让我们更方便、更高效地使用C++语言进行面向对象编程。

以上有六个默认成员函数,但是我们今天只探讨前4个,后面两个实际操作中我们很少直接编写,一般都是使用默认生成式的。

一、构造函数

问题引入:

        在C++编程语言中,构造函数和析构函数的出现与对象及对象的生命周期管理密切相关。在现实世界中,每个事物都有其生命周期,会在某个时候出现也会在另外一个时候消亡。类似地,程序是对现实世界的反映,其中的对象就代表了现实世界的各种事物,自然也就具有生命周期,也会被创建和销毁。

        因此,为了恰当地管理对象的生命周期,特别是对象的初始化和清理工作,C++引入了构造函数和析构函数这两个特殊的成员函数。每一个类都有一个默认的构造函数和析构函数;构造函数在类定义时由系统自动调用,析构函数在类被销毁时由系统自动调用。

具体来说,构造函数主要用于完成对象的初始化工作,它的名字和类名相同,一个类可以有多个构造函数。如果程序员没有手动编写构造函数,编译器会默认生成一个构造函数。另一方面,析构函数则用于完成对象的清理工作,它的名字是类名前面加一个~符号。当对象的生命期结束时,会自动执行析构函数。

        总的来说,构造函数和析构函数的出现,让程序员可以更加方便、准确地管理对象的生命周期,这是C++面向对象编程特性的一个重要体现。

1)构造函数的概念

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次(用一个已经存在的对象去初识另一个和对象)

2)构造函数的特性

不允许出现这正形式的调用:!!!

Date d1();

这种类型的调用,因为它可以被认为成函数的声明,就好比:

Date fun1();

所以,如果想定义一个日期类,则不能加括号,不然括号内需要加参数,

//1普通版
Date()
{m_year = 2023;m_month = 10;m_day = 17;
}
void Date::Init(int year,int month,int day)
{m_year = year;m_month = month;m_day = day;  
}
//2融合版
Date(int year = 2023,int month = 10,int day = 17)
{m_year = year;m_month = month;m_day = day;
}
  1. 函数名与类名相同
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
  5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成
  6. 关于编译器生成的默认成员函数,很多同学会有疑惑:不手动实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来它生成的默认构造函数又没什么用?我们在定义对象时调用了编译器生成的默认构造函数,但是对象的成员变量_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数没有实质性作用吗?

解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,编译器生成默认的构造函数会对自定类型成员调用的它的默认成员函数,而内置类型则不会,这就是为什么全是int类型的成员变量的日期类我们观察不到其初始化的原因。

注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值,自动生成的构造函数会根据此初始化。

class Date
{
private:int _year = 2000;//在此处的默认值可以用作构造函数初始化的参考int _month = 1;int _day = 1;
}

 如果我们用两个栈实现一个队列类就可以体会到默认构造的作用了:

class MyQueue
{
private:Stack _pushst;Stack _popst;
};

二、析构函数

1)析构函数概念

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么清除的呢?析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

2)析构函数特性

  1. 析构函数名是在类名前加上字符 “~”。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数
  5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数
  6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类
  7. 默认生成的析构函数不会对内置类型进行操作,不然释放掉不该释放的内容将会出问题。

析构函数是类的一种特殊的成员函数,其名称与类名相同但增加一个波浪线符号(~)。当对象超出范围或通过调用delete显式销毁对象时,析构函数会自动被调用。析构函数往往用来做“清理善后”的工作,例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存。此外,析构函数也可以有多个,如果没有手动写析构函数,编译器会生成一个默认的析构函数并自动调用。

析构函数的调用时机主要有以下几种:

  1. 对象生命周期结束:当对象超出作用域或被显式销毁时,析构函数会自动被调用,用来释放对象占用的内存空间。
  2. delete操作符:当使用delete删除指针类对象时,会直接调用析构函数来清理内存。
  3. 包含关系:如果对象Dog是对象Person的成员,那么在Person的析构函数被调用时,Dog对象的析构函数也会被自动调用。

三、拷贝构造函数

1)拷贝构造函数概念

        拷贝构造函数,也称为复制构造函数,是一种特殊的构造函数。它是当创建新对象时,使用同一类中之前已创建的对象来初始化新创建的对象。这种构造函数由编译器自动调用,用于一些基于同一类的其他对象的构建及初始化。

拷贝构造函数的形式参数必须是引用,通常为const引用,以便能够处理常量对象和非常量对象的复制。这样,它既能以常量对象(初始化后值不能改变的对象)作为参数,也能以非常量对象作为参数。

        在编程实践中,拷贝构造函数常常用于以下情况:通过使用另一个同类型的对象来初始化新创建的对象;复制对象把它作为参数传递给函数;以及从函数返回一个对象时复制该对象。

        值得注意的是,如果程序员没有显式定义拷贝构造函数,那么编译器会自动生成一个默认的拷贝构造函数。这个默认的拷贝构造函数会将原对象的成员变量值赋值给新对象的相应成员变量。

拷贝构造函数的使用加场景类似于:

//使用a创建b的过程
int a = 1;
int b = a;
  • 直接传值传参(浅拷贝):由于C++的特性会出现析构两次的问题
  • 引用拷贝:但是对于复制对象的操作会影响到原对象(因为引用底层是指针,所以引用拷贝使用时没有问题,但是实际会影响原来的对象)
  • C++规定,自定义类型的传参就需要调用拷贝构造函数

示例代码:

#include <iostream>  
#include <cstring>  class MyClass {  
public:  char* data;  // 构造函数  MyClass(const char* str) {  data = new char[strlen(str) + 1];  strcpy(data, str);  }  // 拷贝构造函数  MyClass(const MyClass& other) {  data = new char[strlen(other.data) + 1]; // 重新分配内存  strcpy(data, other.data); // 深拷贝  }  // 析构函数  ~MyClass() {  delete[] data;  }  // 用于展示数据  void show() const {  std::cout << "Data: " << data << std::endl;  }  
};  // 按值传递  
void byValue(MyClass obj) {  obj.show();  
}  // 按引用传递  
void byReference(MyClass& obj) {  obj.show();  
}  int main() {  MyClass original("Hello, World!");  // 按值传递  std::cout << "Calling byValue:" << std::endl;  byValue(original);  std::cout << "After byValue call, original:" << std::endl;  original.show(); // original 的数据没有改变  // 按引用传递  std::cout << "Calling byReference:" << std::endl;  byReference(original);  std::cout << "After byReference call, original:" << std::endl;  original.show(); // original 的数据没有改变  return 0;  
}

按值传递可能出现的问题

在 byValue 函数中,传递对象会触发拷贝构造函数。若拷贝构造函数不正确(如仅进行浅拷贝),则会导致多个对象指向同一内存。在一个对象被销毁时,另一个对象访问已被释放的内存,进而引发未定义行为。

按引用传递的安全性

在 byReference 函数中,使用引用传递,因此不会发生对象的拷贝。原始对象的状态保持不变,不会出现内存访问的问题。

2)深拷贝

深拷贝,是指源对象与拷贝对象互相独立其中任何一个对象的改动都不会对另外一个对象造成影响。深拷贝会复制所有字段,并复制字段所指向的动态分配内存。深拷贝发生在对象及其引用的对象被复制时。对于基本数据类型,如预定义类型Int32,Double等,深拷贝复制所有基本数据类型的成员变量的值。对于引用数据类型的成员变量,深拷贝申请新的存储空间,并复制该引用对象所引用的对象。

深拷贝是一种特殊的拷贝方式,它不仅复制了对象的基本数据类型成员变量的值,还为引用类型的成员变量申请了新的存储空间,并递归复制了这些引用对象所引用的其他对象。这样,源对象与拷贝对象就完全独立任一对象的修改都不会影响到另一个对象

需要注意的是,在C++中,对于基本类型的数据以及简单的对象,它们之间的拷贝非常简单,通常是按位复制内存。但对于复杂对象和包含指针或动态内存分配的对象来说,需要进行深拷贝来确保两个对象不会相互影响。 默认情况下,基本数据类型(number,string,null,undefined,boolean)的操作都是深拷贝。

3)拷贝构造函数特性

  • 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
  • 拷贝构造函数典型调用场景:使用已存在对象创建新对象函数参数类型为类类型对象函数返回值类型为类类型对象

四、赋值运算符重载

运算符重载

  1. C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。函数名字为:关键字operator后面接需要重载的运算符符号。函数原型:返回值类型 operator操作符(参数列表)
  2. 不能通过连接其他符号来创建新的操作符:比如operator@
  3. 重载操作符必须有一个类类型参数
  4. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  5. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  6. 当然有特殊情况,以下五个运算符不可以重载!!!!
 *     ::    sizeof      ?     : 

赋值运算符重载

我们可以重载赋值运算符。不论形参的类型是什么,赋值运算符都必须定义为成员函数。

                                                                                                                --《C++ Prime》

  1. 赋值运算符重载格式参数类型:const T&,传递引用可以提高传参效率返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值返回*this :要复合连续赋值的含义
  2. 赋值运算符只能重载成类的成员函数不能重载成全局函数//:原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
  3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

在实际编码中,建议在需要动态资源管理的类中实现拷贝构造函数、赋值操作符、析构函数,以遵循“遵循Rule of Three”的原则,确保资源管理正确无误。

如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

且不能改变操作符的操作数。

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

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

相关文章

环丙烷环辛炔聚乙二醇磷脂,淡黄色固体,BCN-PEG-DSPE

中文名称&#xff1a;环丙烷环辛炔聚乙二醇磷脂 英文名称&#xff1a;BCN-PEG-DSPE 外观&#xff1a;通常为黄色或淡黄色固体 材料来源&#xff1a;为华生物 溶解性&#xff1a;在有机溶剂&#xff08;如氯仿、乙醇&#xff09;中具有良好的溶解性&#xff0c;而在水中的溶…

202409电子学会青少年机器人技术等级考试(六级)理论综合真题

青少年机器人技术等级考试理论综合试卷&#xff08;六级&#xff09; 分数&#xff1a; 100 题数&#xff1a; 30 一、 单选题(共 20 题&#xff0c; 共 80 分) 1. 使用 ESP32 for Arduino SPI 类库&#xff0c; 下列选项中&#xff0c; 具有设置时钟模式功能的成员函数是&…

如何学习VBA_3.2.14:字符串的处理

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的劳动效率&#xff0c;而且可以提高数据处理的准确度。我推出的VBA系列教程共九套和一部VBA汉英手册&#xff0c;现在已经全部完成&#xff0c;希望大家利用、学习。 如果…

ABeam News | ABeam中国受邀参加2024中国知识管理年会暨第14届China MIKE颁奖典礼,并荣获大奖

“ABeam/ News ” 近日&#xff0c;2024中国知识管理年会暨第14届China MIKE颁奖典礼圆满召开&#xff0c;大会结合AI赋能新质生产力的热点话题&#xff0c;以“AI超能力KM新价值” 作为主题&#xff0c;为与会观众带来知识管理的一场盛宴。ABeam中国受邀参会并荣获2024 China…

Error: Could not find or load main class org.apache.catalina.startup.Bootstrap

#现象&#xff1a; 官网下载tomcat source包后&#xff0c;启动报错&#xff0c;等一系列缺包造成服务无法启动 Error: Could not find or load main class org.apache.catalina.startup.Bootstrapjava.lang.ClassNotFoundException: org.apache.juli.logging.LogFactory原因 …

论文解读《CTRLsum: Towards Generic Controllable Text Summarization》

引言&#xff1a;一篇上交大佬的著作 ✅ NLP 研 2 选手的学习笔记 笔者简介&#xff1a;Wang Linyong&#xff0c;NPU&#xff0c;2023级&#xff0c;计算机技术 研究方向&#xff1a;文本生成、大语言模型 论文链接&#xff1a;https://aclanthology.org/2022.emnlp-main.396.…

【spotfire】脚本相关

文章目录 ironpython脚本使用JS实现弹出窗口思路实现效果 脚本的使用可以极大扩展spotfire的功能&#xff0c;但如何使用脚本一直不得其门而入&#xff0c;咨询厂商、查询资料&#xff0c;特此记录备忘。 ironpython脚本使用 参见官网教程&#xff1b; 部分参考资料如下&#…

嵌入式硬件杂谈(一)-推挽 开漏 高阻态 上拉电阻

引言&#xff1a;对于嵌入式硬件这个庞大的知识体系而言&#xff0c;太多离散的知识点很容易疏漏&#xff0c;因此对于这些容易忘记甚至不明白的知识点做成一个梳理&#xff0c;供大家参考以及学习&#xff0c;本文主要针对推挽、开漏、高阻态、上拉电阻这些知识点的学习。 目…

RCAgent:云故障根因分析的自主智能体工具增强型大模型

人工智能咨询培训老师叶梓 转载标明出处 由于云上计算部署的不断扩展&#xff0c;手动在线异常RCA工作流程&#xff0c;如创建故障排除工具&#xff0c;常常使网站可靠性工程师&#xff08;SRE&#xff09;应接不暇。为了提高云服务可靠性效率&#xff0c;一系列人工智能运维&…

PET-文件包含-FINISHED

include发生错误报warning&#xff0c;继续执行。require发生错误直接error&#xff0c;不继续执行 无视扩展名&#xff0c;只要能解析&#xff0c;就能当可执行文件执行&#xff0c;哪怕文件后缀或没后缀 1 条件竞争 pass17 只需要知道tmp的路径。把xieshell.jpg上传&…

基于Java+SpringBoot+Vue前后端分离课程管理系统

一、作品包含 源码数据库设计文档全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据库&…

学术界的秘密武器:Zotero7大插件推荐

还在为海量文献管理头疼吗?还在为找不到合适的插件犯愁吗?别急,今天我就要带你解锁Zotero的终极武器 - 那些让你爱不释手的必备插件! 作为一个从小白到文献管理达人的过来人,我可以负责任地说:没有这些插件,你的Zotero只能发挥一半功力!安装了这些插件,你的效率绝对能飙升! …

Linux·进程信号

信号是一种用户、OS、其他进程&#xff0c;向目标进程发送异步事件的一种方式。 在系统中信号是OS出场时程序员就内置好了的&#xff0c;因此任何进程都认识所有信号&#xff0c;信号产生之前&#xff0c;信号的处理方案就已经设定好了&#xff0c;一般有三种 1. 默认行为 2.…

BizDevOps:从理念到实践,贯通企业全链路协同

&#x1f446; 点击蓝字 关注我们 引言 BizDevOps的概念由DevOps发展和进化而来&#xff0c;其目标超越了开发和运维的协同&#xff0c;进一步实现业务、研发和运维的全链条协作&#xff0c;让业务作为价值的起点及核心目标。 BizDevOps的核心驱动力在于解决效率和正确性上的割…

工厂方法模式和抽象工厂模式

序 本文主要是记录学习设计模式当中的工厂方法和抽象工厂时碰到的疑惑和对答案的探讨 刚接触时的工厂方法模式和抽象工厂模式 工厂方法模式 类图 代码 //工厂public interface TVFactory {TV produce(); }public class TclTVFactory implements TVFactory{Overridepublic T…

NVR小程序接入平台EasyNVR多品牌NVR管理工具/设备:RTMP协议摄像头的接入

随着安防技术的不断进步&#xff0c;越来越多的摄像头开始支持RTMP&#xff08;Real Time Messaging Protocol&#xff09;协议&#xff0c;这种协议使得视频流的实时传输和分发变得更加高效和便捷。NVR小程序接入平台EasyNVR作为一款功能强大的流媒体服务器&#xff0c;支持多…

硬件基础20 数模转换器D/A DAC

目录 一、DAC基本原理 二、倒T形电阻网络D/A转换器 三、权电流型D/A转换器 四、重要技术指标与参数 1、分辨率/位数 2、转换精度 &#xff08;1&#xff09;、比例系数误差 &#xff08;2&#xff09;、失调误差 3、转换速度 4、温度系数 五、DAC的应用 1、数字式可…

Memory consistency model 梳理目录

(图片来源&#xff1a;https://mp.weixin.qq.com/s/uz4fZgJSRNm-MIRdXgBMmw) 闲聊内存模型(Memory Model)https://blog.csdn.net/zhangshangjie1/article/details/143743250?sharetypeblogdetail&sharerId143743250&sharereferPC&sharesourcezhangshangjie1&…

WPF Prism框架

Prism 是一个开源框架&#xff0c;专门用于开发可扩展、模块化和可测试的企业级 XAML 应用程序&#xff0c;适用于 WPF&#xff08;Windows Presentation Foundation&#xff09;和 Xamarin Forms 等平台。它基于 MVVM&#xff08;Model-View-ViewModel&#xff09;设计模式&am…

智能零售柜商品识别

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于CNN-RNN的影像报告生成】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实现…