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

类和对象(2)

一.类的默认成员函数

默认成员函数就是用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。

它们主要有以下几个:

(补充说明:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的原生数据类型,如:int、char、double、指针等;自定义类型就是我们使用class、struct等关键字自己定义的类型。)

二.构造函数

构造函数是C++类的特殊成员函数,作用是在创建对象时完成初始化。(即数据结构中的Init)
构造函数的特点:
   1.名称与类名完全相同。
  2. 无返回值(包括不写void)。

    3.对象实例化时系统会自动调用对应的构造函数。
   4. 构造函数可以重载。
   5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义,编译器将不再生成。


 作用:
   为对象的数据成员设置初始值,确保对象状态有效。
  可执行资源分配等初始化操作(如打开文件、分配内存)。
 
分类:
   默认构造函数:无参数,若用户未定义任何构造函数,编译器自动生成,对成员做默认初始化(基本类型值不确定,类类型调用自身默认构造函数)。
    带参数构造函数:通过参数灵活初始化对象,支持不同创建方式(如根据传入数据设定成员值)。
 
   构造函数可重载(参数列表不同),创建对象时根据参数匹配对应函数。

 以上的段C++ 代码,其中  Date  类的构造函数是关键。代码里定义了  Date  类的默认构造函数  Date()  ,它在创建对象时自动调用。当  main  函数中创建  Date d1;  时,默认构造函数发挥作用,将  _year  初始化为  1  , _month  初始化为  2  , _day  初始化为  2  。

 默认构造函数的存在确保了对象在创建时能获得一组确定的初始值,让对象处于一个合理的初始状态。而另一个对象  d2  ,虽也先由默认构造函数创建,但之后通过  Init  函数重新设置了日期,这体现了构造函数与其他成员函数配合,可灵活控制对象的初始化及后续状态调整。

 

三.析构函数

析构函数与构造函数功能相反。析构函数并非完成对对象本身的销毁,例如局部对象存于栈帧中,函数结束栈帧销毁时它便自动释放,无需额外处理。C++规定对象在销毁时会自动调用析构函数,用于完成对象中资源的清理释放工作。(即数据结构中的Destroy)

析构函数的特点:

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

2. 无参数无返回值。(这里跟构造类似,也不需要加void)

3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。

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

5. 跟构造函数类似,我们不写编译器自动生成的析构函数对内置类型成员不做处理,自定义类型成员会调用它的析构函数。

6. 还需要注意的是我们显式写析构函数,对于自定义类型成员也会调用它的析构,也就是说自定义类型成员无论什么情况都会自动调用析构函数。

7. 如果类中没有申请资源,析构函数可以不写,直接使用编译器生成的默认析构函数(如Date);若默认析构函数适用,也无需显式定义(如MyQueue);但当类中申请了资源时,必须自定义析构函数,否则会导致资源泄漏(如Stack)。
8. 在一个局部作用域内的多个对象,C++规定后定义的对象先析构。

 以上代码C++ 代码的 Stack 类中,构造函数 Stack(int n = 4) 意义重大。它通过 malloc 为栈的数据存储区动态分配内存,参数 n 可指定初始容量,若内存分配失败,及时报错返回,保障程序健壮性。这是栈对象初始化的关键步骤,让对象拥有可用资源。

 析构函数 ~Stack() 则是资源回收的保障。当 Stack 对象生命周期结束,像 main 函数中 st1 超出作用域时,析构函数自动调用。它释放构造函数申请的内存,将指针置空,相关变量归零,防止内存泄漏,确保系统资源合理回收。二者配合,实现了栈对象生命周期内资源从获取到释放的完整、正确管理。

不需要写构造函数与析构函数的例子:

 以上 Myqueue 类(解释:Muqueue类表示的是用两个栈实现队列)中未显式定义构造函数和析构函数,原因如下:
 1. 构造函数: Myqueue 类包含两个 Stack 类型的成员变量 pushst 和 popst  。当 Myqueue 对象创建时,编译器自动生成的默认构造函数会调用 Stack 类的默认构造函数,对这两个成员变量进行初始化,满足了基本初始化需求,所以无需额外定义。


2.析构函数:对象生命周期结束时,编译器自动生成的默认析构函数会依次调用成员变量 pushst 和 popst  对应的 Stack 类析构函数,完成资源释放等清理工作,不存在资源泄漏等问题,因此不必手动编写。

 

四.拷贝构造函数

拷贝构造函数是C++中构造函数的特殊形式,当构造函数的首个参数为自身类类型的引用,且其余参数均有默认值时,它便属于拷贝构造函数。其具有以下特性:
 1. 函数重载:拷贝构造函数是构造函数的一种重载形式。

2. 参数要求:首个参数必须是类类型对象的引用。若采用传值方式,会因语法逻辑上的无穷递归调用导致编译器报错。它也可拥有多个参数,但首个参数需为类类型对象引用,后续参数必须有默认值。

3. 拷贝行为规定:C++要求自定义类型对象进行拷贝操作时,必须调用拷贝构造函数。所以,自定义类型在传值传参和传值返回时,都会触发拷贝构造函数的调用。

4. 编译器生成规则:若未显式定义拷贝构造函数,编译器会自动生成。对于内置类型成员变量,自动生成的拷贝构造函数执行值拷贝(即按字节逐个拷贝) ;对于自定义类型成员变量,则会调用其自身的拷贝构造函数。例如, Date 类成员全为内置类型且不涉及资源指向,编译器自动生成的拷贝构造函数就能满足需求,无需手动实现;而 Stack 类虽成员也是内置类型,但存在指针 _a 指向资源,自动生成的浅拷贝方式无法满足要求,需手动实现深拷贝(对指向资源也进行拷贝) ; MyQueue 类内部主要是自定义类型 Stack 成员,编译器自动生成的拷贝构造函数会调用 Stack 的拷贝构造函数,因此也无需手动实现。一般来说,若类显式实现了析构函数并释放资源,就需显式编写拷贝构造函数,反之则通常不必。       

5. 返回值相关:传值返回会创建临时对象并调用拷贝构造函数;传引用返回则返回对象的别名,不产生拷贝。但如果返回对象是函数局部作用域内的局部对象,函数结束后该对象会被销毁,此时使用引用返回会产生野引用(类似野指针) 。所以,传引用返回虽能减少拷贝,但必须保证返回对象在函数结束后依然存在。

    为什么要传引用调用?

在拷贝构造函数中要传引用而不能传值调用,原因如下:                                                                            - 避免无穷递归:传值调用时,实参会将值拷贝给形参。若拷贝构造函数参数是按值传递,那么调用拷贝构造函数时,为了生成这个形参对象,又会调用拷贝构造函数(因为传值本质是一次拷贝) ,如此循环往复,就会形成无穷递归。就像图中展示的,每次调用拷贝构造函数前传值传参的拷贝行为,不断触发新的拷贝构造调用,导致程序崩溃。

- 提高效率:传引用不会产生新的对象拷贝,直接操作原始对象的引用,相比传值调用避免了不必要的对象复制开销,尤其是对于复杂对象,能显著提升程序运行效率。

     拷贝构造函数的调用

  拷贝构造函数定义                                                         代码中 Date(const Date& d)  是 Date 类的拷贝构造函数,以引用形式接收参数,避免传值调用带来的无穷递归问题。它将传入对象 d 的成员变量 _year  、 _month  、 _day  赋值给当前对象,实现对象间数据成员的拷贝。

 

拷贝构造函数调用                                            在 main 函数中, Date d2(d1);  语句调用了拷贝构造函数,将 d1 对象的数据成员拷贝给新创建的 d2 对象,使得 d2 和 d1 数据一致,之后 d2.Print()  输出与 d1 相同的日期信息。 而 Date(Date* d)  并非拷贝构造函数(拷贝构造函数参数应为类对象引用),但也能实现从一个对象指针获取数据来初始化当前对象。

在拷贝构造函数中:对比传引用与传指针

五.运算符重载

  (1)  当运算符被用于类类型的对象时,C++ 允许对类类型对象进行运算符重载,赋予运算符新含义。若类类型对象使用运算符时无对应重载,会编译报错。以下是运算符重载要点:
 
(2)函数特性
运算符重载函数名字由  operator  与要重载的运算符组成,和普通函数一样,有返回类型、参数列表和函数体。
 
(3)参数规则
 ①一元运算符重载函数有一个参数;二元运算符重载函数有两个参数,左侧运算对象传第一个参数,右侧传第二个。
②若重载为成员函数,首个运算对象由隐式  this  指针接收,参数比运算对象少一个 。
 
(4)重载限制
 ①重载后运算符优先级和结合性与内置类型运算符一致,不能创造语法中不存在的新运算符,如  operator@  。
② .* 、 :: 、 sizeof 、 ?: 、 .  这 5 个运算符不能重载 。
③重载操作符至少有一个类类型参数,不能改变内置类型对象的运算含义 。
 
(6)特殊情况
 ①对于  Date  类等,要根据实际意义决定重载哪些运算符,如  Date  类重载  operator-  可能有意义,而  operator+  可能无意义。
②重载  ++  运算符时,前置  ++  和后置  ++  函数名都为  operator++  。为区分,后置  ++  重载函数增加一个  int  形参 。
③<<  和  >>  运算符需重载为全局函数。若重载为成员函数, this  指针会占据左侧运算对象位置(第一个形参),导致调用形式如  对象<<cout  不符合习惯。                                                       ④全局重载时, ostream/istream  放第一个形参,类类型对象放第二个 。

小练习:operator==来判断日期是否相等

  

 

 以上C++ 代码,定义了  Date  类,包含构造函数来初始化年、月、日。还重载了  ==  运算符用于判断两个日期是否相等 。在  main  函数中创建了两个  Date  对象,并通过调用重载的运算符进行比较,最终输出比较结果,清晰展示了运算符重载在日期比较场景中的应用。

相关文章:

  • C# 中 INI 文件操作扩展类:轻松管理配置文件
  • 算法-二进制运算
  • 通过Jflash合并Boot和App两个hex为一个hex的办法
  • Instruct模型 AutoModelForCausalLM :智能指令执行专家
  • 【力扣】关于链表索引
  • ArrayBlockingQueue 和 LinkedBlockingQueue 有什么区别?
  • SpringAI--RAG知识库
  • 网络安全--PHP第三天
  • 一、docker安装以及配置加速
  • 动态库版本不配问题排查步骤
  • 【QT】TXT文件的基础操作
  • 国外常用支付流程简易说明(无代码)
  • NoteGen 如何使用 AI 进行记录
  • orm详解--查询执行
  • c++算法题
  • 红海云荣膺2025人力资源科技影响力品牌30强
  • AI时代新词-机器学习即服务(MLaaS)
  • python:机器学习(KNN算法)
  • 2021年江西工业互联网大赛———工业固件分析
  • 【Linux系统】Linux基础指令(一)
  • 建设宠物店网站/免费访问国外网站的app
  • 建设工程竞标网站/友情链接交换网址大全
  • 怎么查网站是哪家制作公司做的/建站优化公司
  • 58同城北京网站建设/百度怎么推广自己的信息
  • 洛阳网站seo/百度seo泛解析代发排名
  • 免费的黄金网站有哪些/关键词快速上首页排名