类和对象(中)(1)

类和对象(中)(1)

在这里插入图片描述

类的默认成员函数

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

⼀个类,我们不写的情况下编译器会默认⽣成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最后两个取地址重载不重要,稍微了解⼀下即可。

其次就是C++11以后还会增加两个默认成员函数,移动构造和移动赋值,这个以后再说。默认成员函数很重要,也比较复杂,我们要从两个方面去学习:

• 第一:我们不写时,编译器默认生成的函数⾏为是什么,是否满⾜我们的需求。

• 第二:编译器默认⽣成的函数不满⾜我们的需求,我们需要自己实现,那么如何自己实现?

注意,我们说C++复杂,其实从这一部分就开始体现。这一部分:知识点很多,彼此间千丝万缕关系,要注意的点很多。

构造函数

(最复杂的一个)

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象(我们常使用的局部对象是栈帧创建时,空间就开好了),⽽是对象实例化时初始化对象。构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能,构造函数⾃动调⽤的特点就完美的替代的了Init。

构造函数的特点

我们先来理解这四点。

  1. 函数名与类名相同。
  2. ⽆返回值。(返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)
  3. 对象实例化时系统会自动调用对应的构造函数。
  4. 构造函数可以重载。
  5. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦用户显式定义编译器将不再⽣成。
#include<iostream>
using namespace std;
class Date
{
public:// 1.⽆参构造函数 Date()//函数名和类名相同,没有返回值连void都没有{_year = 1;_month = 1;_day = 1;}// 2.带参构造函数 Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{Date d1; // 调用了默认构造函数 // 注意:如果通过⽆参构造函数创建对象时,对象后⾯不⽤跟括号,否则编译器⽆法区分这⾥是函数声明还是实例化对象 Date d2(2025, 1, 1); // 调⽤带参的构造函数的写法return 0;
}

然后

#include<iostream>
using namespace std;
class Date
{
public:// 1.⽆参构造函数 //Date()//函数名和类名相同,没有返回值连void都没有//{//    _year = 1;//    _month = 1;//    _day = 1;//}// 2.带参构造函数 //Date(int year, int month, int day)//{//    _year = year;//    _month = month;//    _day = day;//}// 3.全缺省构造函数 //和第一种构成函数重载,但是会产生歧义,所以不能同时存在Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};int main()
{Date d1; // 调用了默认构造函数 Date d2(2025, 1, 1); // 调⽤带参的构造函数的写法Date d3(2024);//调用了全缺省的构造函数return 0;
}

然后

  1. ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数都叫做默认构造函数。但是这三个函数有且只有⼀个存在,不能同时存在

    ⽆参构造函数和全缺省构造函数虽然构成函数重载,但是调⽤时会存在歧义。要注意很多人会认为默认构造函数是编译器默认⽣成那个叫默认构造,实际上⽆参构造函数、全缺省构造函数也是默认构造,总结⼀下就是不传实参就可以调用的构造就叫默认构造!(可以理解为“无实参构造”)

  2. 我们不写,编译器默认生成的构造,对内置类型成员变量的初始化没有要求,也就是说是否初始化是不确定的,看编译器。(早期的编译器对于内置类型都不初始化,也就是随机值)(建议当做不处理)

    对于⾃定义类型成员变量,要求调⽤这个成员变量的默认构造函数(注意有三种)初始化。如果这个成员变量,没有默认构造函数,那么就会报错,我们要初始化这个成员变量,需要⽤初始化列表才能解决,初始化列表,我们之后再说。

说明:C++把类型分成内置类型(基本类型)和⾃定义类型。内置类型就是语⾔提供的原⽣数据类型, 如:int/char/double/指针等,⾃定义类型就是我们使⽤class/struct等关键字⾃⼰定义的类型。

大多数情况下我们不写,编译器默认生成的函数不能满足我们的需求。所以大多数情况下构造函数需要我们自己写。

下面的这个场景中,编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化 ,是一个默认构造有用的例子

#include<iostream>
using namespace std;typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}// ...
private:STDataType* _a;size_t _capacity;size_t _top;
};// 两个Stack实现队列 
class MyQueue
{
public://编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化 
private:Stack pushst;Stack popst;//int size;//如果监视我们可以看到是0不是随机值,这里倒是编译器处理了内置类型。但是是个坑,以后再说解决方法。
};int main()
{MyQueue mq;return 0;
}
>总结:

大多数情况下构造函数都需要自己实现,少数情况类似MyQueue且Stack有默认构造时,MyQueue自动生成的就可以用。(等一个后续C++11增加的缺省值的概念)

构造函数,应写尽写。

析构函数

(可以感受到设计的好处)

析构函数与构造函数功能相反,析构函数不是完成对对象本身的销毁,⽐如局部对象是存在栈帧的, 函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会⾃动调⽤析构函数,完成对象中资源的清理释放工作。析构函数的功能类⽐我们之前Stack实现的Destroy功能,⽽像Date没有 Destroy,其实就是没有资源需要释放,所以严格说Date是不需要析构函数的。

我们在main中定义一个对象,是在编译时系统就开好空间了。销毁也是在main结束时和函数栈帧一起销毁。所以空间销毁不是我们担心的问题,也就是“析构函数不是完成对对象本身的销毁”。

析构函数的特点
  1. 析构函数名是在类名前加上字符**~**。
  2. ⽆参数⽆返回值(就不会存在重载情况了)。(这⾥跟构造类似,也不需要加void)
  3. ⼀个类只能有⼀个析构函数。若未显式定义,系统会⾃动⽣成默认的析构函数。
  4. 对象⽣命周期结束时,系统会⾃动调⽤析构函数。
  5. 跟构造函数类似,我们不写,编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会调⽤他的析构函数。
#include<iostream>
using namespace std;typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}~Stack(){free(_a);_a = nullptr;_top = _capacity = 0;}private:STDataType* _a;size_t _capacity;size_t _top;
};int main()
{Stack st1;//只要写这一句return 0;
}

可以看到,对象实例化时系统会自动调用对应的构造函数。我们完成了对象初始化。

再看,对象⽣命周期结束时,系统会⾃动调⽤析构函数。

所以我们可以发现,原本我们需要显示去调用函数Init和Destroy,现在都不需要了,我们只写一句 Stack st1;就会在实例化时自动初始化,在main函数栈帧要销毁时(也就是对象生命周期结束时)自动释放对象的资源。

  1. 还需要注意的是我们显⽰写析构函数,对于自定义类型成员也会调⽤他的析构,也就是说自定义类型成员⽆论什么情况都会⾃动调⽤析构函数。(怕出现内存泄漏)

  2. 如果类中没有申请资源时,析构函数可以不写,直接使⽤编译器⽣成的默认析构函数,如Date;

    如果默认⽣成的析构就可以⽤,也就不需要显⽰写析构,如MyQueue;

    但是有资源申请时,⼀定要⾃⼰写析构,否则会造成资源泄漏,如Stack。

  3. ⼀个局部域的多个对象,C++规定后定义的先析构。(栈帧的性质就像数据结构中的栈,要求后进先出)

也就是析构函数我们只需要去管内置类型中有资源申请的,自定义类型的成员可以不管。

》在学习栈的时候我们曾经做过一道匹配括号的题目(没做过的可以跳过该部分)

// ⽤之前C版本Stack实现 
bool isValid(const char* s) {ST st;STInit(&st);while (*s){// 左括号⼊栈 if (*s == '(' || *s == '[' || *s == '{'){STPush(&st, *s);}else // 右括号取栈顶左括号尝试匹配 {if (STEmpty(&st)){STDestroy(&st);return false;}char top = STTop(&st);STPop(&st);// 不匹配 if ((top == '(' && *s != ')')|| (top == '{' && *s != '}')|| (top == '[' && *s != ']')){STDestroy(&st);return false;}}++s;}// 栈不为空,说明左括号⽐右括号多,数量不匹配 bool ret = STEmpty(&st);STDestroy(&st);return ret;
}int main()
{cout << isValid("[()][]") << endl;cout << isValid("[(])[]") << endl;return 0;
}
// ⽤最新加了构造和析构的C++版本Stack实现 
bool isValid(const char* s) {Stack st;//无需调用Init,会自动调用构造函数,防忘while (*s){if (*s == '[' || *s == '(' || *s == '{'){st.Push(*s);//this隐式传了,无需传st地址}else{// 右括号⽐左括号多,数量匹配问题 if (st.Empty()){return false;//无需调用Destroy,自动调用析构函数,防忘}// 栈⾥⾯取左括号 char top = st.Top();st.Pop();// 顺序不匹配 if ((*s == ']' && top != '[')|| (*s == '}' && top != '{')|| (*s == ')' && top != '(')){return false;}}++s;}// 栈为空,返回真,说明数量都匹配 左括号多,右括号少匹配问题 return st.Empty();//可以直接return,也是因为会自动调用析构函数
}

可以看到,自动调用真的很好。

赋值运算符重载

运算符重载
  • 当运算符被⽤于类类型的对象时,C++语⾔允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使⽤运算符时,必须转换成调⽤对应运算符重载(函数),若没有对应的运算符重载,则会编译报错。

自定义类型的比较应该由我们自己定义而不是系统定义。

  • 运算符重载是具有特殊名字的函数,他的名字是由operator和后⾯要定义的运算符共同构成(构成了函数名)。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体
  • 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数,二元运算符有两个参数,二元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第二个参数。
//以比较日期类为例
bool operator==(Date d1,Date d2)
{return d1._year==d2.year&&d1._month==d2._month&&d1._day==d2._day;
}
bool operator<(Date d1,Date d2)
{}int main()
{Date x1(2024,7,10);Date x2(2024,7,10);operator==(x1,x2);//显式调用x1 == x2;//会转换成上面调用函数return 0;
}

这时有一个问题,在函数operator==内部我们不能访问私有的类成员。

解决办法:

直接变为公有的方法显然不好。

我们可以提供Get函数:

//在类中
int GetYear()
{return _year;
}

还有一种友元方式,这个以后再说。

还有一种方法,我们重载本就是函数,可以直接放到类里面,变为成员函数。

  • 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数⽐运算对象少⼀个。

就变成这样写

//在类中
bool operator==(Date d2)
{return _year==d2.year&& _month==d2._month&& _day==d2._day;
}int main()
{x1.operator==(x2);x1==x2;//这样写会转换成上面return 0;
}

c++
//在类中
int GetYear()
{
return _year;
}


还有一种友元方式,这个以后再说。还有一种方法,我们重载本就是函数,可以直接放到类里面,变为成员函数。* 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数⽐运算对象少⼀个。就变成这样写```c++
//在类中
bool operator==(Date d2)
{return _year==d2.year&& _month==d2._month&& _day==d2._day;
}int main()
{x1.operator==(x2);x1==x2;//这样写会转换成上面return 0;
}

本文到此结束,类和对象(中)还没有讲完,敬请期待后文=_=

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

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

相关文章

云计算实训24——python基本环境搭建、变量和数据类型、数据集合、py脚本

一、python环境搭建 确保拥有阿里云镜像 查看python环境 [rootpython ~]# yum list installed | grep python 查看epel是否安装 [rootpython ~]# yum list installed | grep epel 安装epel [rootpython ~]# yum -y install epel-release.noarch 查看是否安装python3 [rootpyt…

【数据结构】mapset详解

&#x1f341;1. Set系列集合 Set接口是一种不包含重复元素的集合。它继承自Collection接口&#xff0c;所以可以使用Collection所拥有的方法&#xff0c;Set接口的实现类主要有HashSet、LinkedHashSet、TreeSet等&#xff0c;它们各自以不同的方式存储元素&#xff0c;但都遵…

OceanBase V4.2特性解析:MySQL模式下GIS空间表达式的场景及能力解析

1. 背景 1.1. OceanBase Mysql gis空间表达式的应用场景及能力 在OceanBase 4.1版本中&#xff0c;mysql模式下支持了gis数据类型以及部分空间对象相关的表达式&#xff0c;随着客户使用空间数据的需求日益增长&#xff0c;需要快速地补齐空间数据存储和计算分析的能力&#…

实景三维:解锁地理信息新维度,引领未来城市智慧之钥

在这个信息爆炸与科技日新月异的时代&#xff0c;地理信息与遥感技术正以前所未有的速度改变我们认知世界的方式。在推动“实景三维平台”这一前沿科技的构建上&#xff0c;它不仅是地理信息的立体呈现&#xff0c;更是智慧城市的基石&#xff0c;打开了通往未来城市规划、管理…

C++设计模式(代理模式)

1. 电话虫 在海贼中&#xff0c;有一种神奇的通信工具叫做电话虫&#xff08;Den Den Mushi&#xff09;&#xff0c;外形如蜗牛&#xff0c;身上带有斑点或条纹或通体纯色&#xff0c;壳顶上有对讲机或按键&#xff0c;不接通时会睡觉&#xff0c;接通时会惊醒&#xff0c;并发…

数据结构之链表

写在前面 链表是一种常用的线性数据结构&#xff0c;在jdk中也提供具体的实现类java.util.LinkedList。本文来看下其相关内容。 1&#xff1a;链表的特点 链表是一种由很多个节点组成的线性数据结构&#xff0c;每个节点都有一个指向下一个节点的引用&#xff0c;从而构成链…

Unity(2022.3.38LTS) - 下载,安装

目录 A. 简介 B. 下载和安装UnityHub C. 下载安装unity编辑器 安装页面 选择版本 添加模块 D.总结 A. 简介 Unity 是一款广泛使用的跨平台游戏开发引擎。 一、主要特点 跨平台性&#xff1a; 支持多种主流平台&#xff0c;包括 Windows、Mac、Linux、iOS、Android、Xb…

LeetCode_sql_day15(262.行程与用户)

描述&#xff1a;262. 行程和用户 - 力扣&#xff08;LeetCode&#xff09; 取消率 的计算方式如下&#xff1a;(被司机或乘客取消的非禁止用户生成的订单数量) / (非禁止用户生成的订单总数)。 编写解决方案找出 "2013-10-01" 至 "2013-10-03" 期间非禁止…

Vue 应用实例的关键方法与配置案例一

目录 createApp createSSRApp app.mount app.unmount app.component app.directive Vue3.X自定义全局指令 Vue2.X自定义全局指令 app.use app.mixin 非 VIP 用户能够免费下载博文资源 createApp createApp 是 Vue 3.0 中用于创建应用实例的方法。它接收一个…

127. Go反射基本原理

文章目录 反射基础 - go 的 interface 是怎么存储的&#xff1f;iface 和 eface 的结构体定义&#xff08;runtime/iface.go&#xff09;&#xff1a;_type 是什么&#xff1f;itab 是什么&#xff1f; 反射对象 - reflect.Type 和 reflect.Value反射三大定律Elem 方法reflect.…

mysql中select的执行流程

目录 引言 SELECT查询语句的重要性 ​编辑引言部分重写示例&#xff1a; MySQL架构概览 MySQL架构概述 Server层的核心功能模块 知识点图文结合示例&#xff1a; 连接器的作用 连接器的职责 连接器职责 查询缓存的工作原理 查询缓存的概念 查询缓存的工作机制 查询…

虚幻引擎 C++ 实现平面阴影

1、平面阴影介绍 平面阴影是一种相对简单的渲染阴影的方式&#xff0c;可以理解为对一个模型渲染两次&#xff0c;一次是渲染模型本身&#xff0c;另一次是渲染模型的投影。渲染投影可以看作是将模型的顶点变换到地面的投影空间再渲染&#xff0c;可以理解为渲染了一个“压扁”…

Linux内核编程(十二)热插拔

本文目录 一、知识点1. 热插拔概念2. 热插拔机制3. Netlink机制 二、内核发送uevent事件到用户空间1. kobject发送uevent事件2. udevadm命令查看★示例代码&#xff1a;★优化&#xff1a;完善kset_uevent_ops&#xff08;热插拔事件结构体&#xff09; 三、用户空间使用Netlin…

Dubbo源码深度解析(四)

接上篇博客《Dubbo源码深度解析(三)》&#xff0c;上篇博文&#xff0c;主要讲的是DubboBootstrap#start()方法中调用到的其他方法&#xff0c;以及讲到ServiceConfig#export()方法的调用链路。其中讲到最核心的方法为ServiceConfig#doExportUrlsFor1Protocol()&#xff0c;还没…

CentOS7 配置 nginx 和 php 方案

配置方案 一、安装软件二、编写配置文件&#xff0c;连接PHP三、引用文件四、测试 鉴于网上教程错综复杂&#xff0c;写下一这篇文章 本教程只需要三步即可 一、安装软件 yum install -y nginx php php-fpm二、编写配置文件&#xff0c;连接PHP 一般情况下在安装完 nginx 后…

python-质因数分解(赛氪OJ)

[题目描述] 已知正整数 n 是两个不同的质数的乘积&#xff0c;试求出两者中较大的那个质数。输入格式&#xff1a; 输入一个正整数 n。输出格式&#xff1a; 输出一个正整数 p&#xff0c;即较大的那个质数。样例 #1样例输入 #1 21样例输出 #1 7提示&#xff1a; 1≤n≤2109 来…

无字母数字的绕过方法

php代码 <?phpif(isset($_GET[code])){$code $_GET[code];if(strlen($code)>35){die("Long.");}if(preg_match("/[A-Za-z0-9_$]/",$code)){die("NO.");}eval($code);}else{highlight_file(__FILE__);} 题目的限制&#xff1a; webshell…

书籍分享:【矩阵力量】豆瓣评分高达9.6,看完感叹《矩阵论》又白学了

书籍分享&#xff1a;【矩阵力量】豆瓣评分高达9.6&#xff0c;看完感叹《矩阵论》又白学了 《矩阵力量》简要介绍书籍下载链接 《矩阵力量》简要介绍 《矩阵力量》是姜伟生精心编写的线性代数的深度理解之作&#xff0c;作者将抽象的线性代数概念用通俗易懂的语言和大量生动形…

Windows下,C# 通过FastDDS高效通信

目录 1、安装FastDDS 库2、使用IDL定义自己的数据格式3、生成DLL3.1 托管 &#xff08;Managed&#xff09;模式3.2 非托管 &#xff08;Unmanaged&#xff09;模式 -- 可用于Unity 代码示例 eprosima Fast DDS is a C implementation of the DDS (Data Distribution Service) …

vscode用快捷键一键生成vue模板

项目中有些代码模块是固定的&#xff0c;如下面的代码所示&#xff0c;为了不重复写这些相同的代码&#xff0c;我们可以使用快键键一键生成模板。 流程&#xff1a; 中文&#xff1a;首选项-> 用户代码片段 -> 输入框中输入vue,找到vue.json文件&#xff08;没有vue.j…