【C++】类和对象(中)--上篇

在这里插入图片描述
个人主页~
类和对象上


类和对象

  • 一、类的六个默认成员函数
  • 二、构造函数
    • 1、构造函数基本概念
    • 2、构造函数的特性
  • 三、析构函数
    • 1、析构函数的概念
    • 2、特性
  • 四、拷贝构造函数
    • 1、拷贝构造函数的概念
    • 2、特征

一、类的六个默认成员函数

如果有个类中什么成员都没有,那么被称为空类

由编译器自动生成的成员函数称为默认成员函数

空类中会自动生成六个默认成员函数,这六个默认成员函数在每个类中都会自动生成

①初始化功能的构造函数
②清理功能的析构函数
③使用同类对象初始化创建对象的拷贝构造
④把一个对象赋值给另一个对象的赋值重载
⑤对普通对象取地址重载
⑥对const对象取地址重载

这六个默认成员函数主要将操作对象分为内置类型自定义类型,对二者有不同的操作

二、构造函数

1、构造函数基本概念

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个成员都要初始化,并且在对象整个生命周期内只调用一次

2、构造函数的特性

构造函数是特殊的成员函数,主要任务就是初始化对象
(1)函数名与类名相同
(2)无返回值
(3)对象实例化时编译器自动调用
(4)可以重载

class Date
{
public://无参构造函数Date(){}//带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};

无参调用不用加括号:

//无参调用
Date d1;
//有参调用
Date d2(2024,6,22);

(5)如果类中没有显式定义构造函数,则编译器会自动生成一个无参的默认构造函数,如果有显式定义,编译器将不再生成

class Date
{
public://无参构造函数Date(){}
private:int _year;int _month;int _day;
};

在这里插入图片描述
由编译器自己生成:
在这里插入图片描述
编译器会自动生成一个无参的默认构造函数,初始化给的是随机值
(6)行文至此有人会觉得编译器给的无参的默认构造函数很垃圾,初始化给的是随机值,没有什么意义,他对于自定义类型来说是有很大的意义的,它可以调用自定义类型的默认构造函数

class Time
{
public:Time(){cout << "Time()" << endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:int _year;int _month;int _day;Time _t;
};
int main()
{Date d;return 0;
}

在这里插入图片描述

内置成员变量在类中声明时可以给默认值

class Date
{
private:int _year = 1970;int _month = 1;int _day = 1;
};
int main()
{Date d;return 0;
}

在这里插入图片描述
(7)无参的构造函数、全缺省的构造函数、不写而编译器自动生成的构造函数都叫默认构造函数

全缺省:

class Date
{
public:Date(int year = 1970, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
int main()
{Date d;return 0;
}

在这里插入图片描述

三、析构函数

1、析构函数的概念

析构函数是与构造函数功能相反的一个函数,对象在销毁时会自动调用析构函数,完成资源清理

2、特性

(1)析构函数名是在类名前加上字符~
(2)无参数无返回类型
(3)一个类只能有一个析构函数,未显式定义则自动生成
(4)生命周期结束时自动调用

构造函数+析构函数改造栈:

class Stack
{
public://公共访问,但在类中可以访问private的内容,只是在类外不能直接访问Stack(size_t newcapacity = 4)//缺省{int* tmp = (int*)realloc(a, sizeof(int) * newcapacity);if (tmp == nullptr){perror("realloc fail");exit(-1);}a = tmp;capacity = newcapacity;top = 0;}void Push(int x){a[top++] = x;}void Pop(){top--;}int Top(){return a[top - 1];}bool Empty(){return top == 0;}~Stack(){free(a);a = nullptr;capacity = top = 0;}//自己写的析构函数,在程序执行的最后执行
private://隐私访问int* a;int top;int capacity;
};

编译器自动生成的析构函数不能对内置类型进行资源回收,但可以调用自定义类型的析构函数

class Time
{
public:~Time(){cout << "~Time()" << endl;}//Time的析构函数
private:int _hour;int _minute;int _second;
};
class Date
{
private:int _year = 1970;int _month = 1;int _day = 1;Time _t;
};
int main()
{Date d;return 0;
}

在这里插入图片描述

因为d中包含着四个成员变量,除了_year _month _day 外还有一个Time类,内置类型成员在销毁时不被资源清理,但自定义类型需要调用析构函数回收,但是main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,编译器默认生成析构函数的可以对自定义成员Time调用它的析构函数,即当Date销毁时,Time也会销毁

如果类中没有申请资源时,也就是没有在堆上申请空间时,析构函数可以不写,直接使用编译器生成的默认析构函数,有申请资源的话一定要写,防止资源泄露

四、拷贝构造函数

1、拷贝构造函数的概念

只有单个形参,该形参是对本类类型对象的引用(一般用const修饰),在用已存在的类类型对象创建对象时,由编译器自动调用

2、特征

(1)拷贝构造函数是构造函数的一个重载形式
(2)拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,原因是会引发无穷递归调用

传引用调用:

class Date
{
public:Date(int year = 1970, 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;}
private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(d1);return 0;
}

在这里插入图片描述
传值调用:
在这里插入图片描述
我们知道传值调用形参会开辟一块空间,成为实参的临时拷贝,这样会创建一个Date,因为类会自动调用里面的六个默认成员函数,拷贝构造函数也是其中之一,这样一来,又会创建一个Date,以此类推,无限循环
(3)若未显式定义,编译器会生成默认的拷贝构造函数,这个默认的拷贝构造函数是值拷贝

在编译器生成的默认拷贝函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的

class Time
{
public:Time(){_hour = 1;_minute = 1;_second = 1;}Time(const Time& t){_hour = t._hour;_minute = t._minute;_second = t._second;cout << "Time(const Time & t)" << endl;}
private:int _hour;int _minute;int _second;
};class Date
{
public:Date(int year = 1970, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;Time t;
};int main()
{Date d1;Date d2(d1);//这里是用已有的d1拷贝构造d2return 0;
}

在这里插入图片描述
在这里插入图片描述
(4)编译器默认生成的拷贝构造函数可以拷贝像Date类这样的类,但对于某些类来说我们要显式定义

class Stack
{
public:Stack(size_t newcapacity = 4){int* tmp = (int*)realloc(a, sizeof(int) * newcapacity);if (tmp == nullptr){perror("realloc fail");exit(-1);}a = tmp;capacity = newcapacity;top = 0;}void Push(int x){a[top++] = x;}void Pop(){top--;}int Top(){return a[top - 1];}bool Empty(){return top == 0;}~Stack(){free(a);a = nullptr;capacity = top = 0;}
private:int* a;int top;int capacity;
};int main()
{Stack s1;s1.Push(1);s1.Push(2);Stack s2(s1);return 0;
}

该程序中没有显式定义拷贝构造函数吗,是调用的编译器自动生成的拷贝构造函数
在这里插入图片描述
在执行析构函数的时候出现了错误,这里的原因是数组a已经被释放了,再次释放产生错误

因为编译器自动生成的拷贝构造函数是值拷贝,所以在生成s2时,s2中的指针a指向的数组与s1中的指针指向的数组相同,在程序结束时,调用析构函数释放了s2,对应的这块数组空间也被释放,然后调用析构函数释放s1,已经被释放的空间不能被再次释放,所以出现了这样的错误,所以我们需要自己显式定义一个拷贝构造函数

深拷贝:

Stack(const Stack& s)
{cout << "Stack(const Stack& s)" << endl;int* a = (int*)malloc(sizeof(int) * capacity);if (a == nullptr){perror("malloc fail");exit(-1);}memcpy(a, s.a, sizeof(int) * s.top);top = s.top;capacity = s.capacity;
}

在这里插入图片描述
(5)拷贝构造函数的使用场景:

已存在的对象建立新对象

函数参数为类类型对象

函数返回值为类类型对象

class Date
{
public:Date(int year, int month, int day){cout << "Date(int,int,int):" << this << endl;}Date(const Date & d){ cout << "Date(const Date& d):" << this << endl;}~Date(){cout << "~Date():" << this << endl;}
private:int _year;int _month;int _day;
};Date Test(Date d)
{Date temp(d);return temp;
}int main()
{Date d1(2022, 1, 13);Test(d1);return 0;
}

因为我所使用的编译器为VS2022,是一种新的编译器,会对程序有一定的优化
我们来分析一下,如果编译器不优化,打印出的结果是什么
①创建d1类,调用构造函数,打印"Date(int,int,int):"和d1的地址

②以d1拷贝构造形参d,调用拷贝构造函数,打印"Date(const Date& d):"和d的地址

③以d拷贝构造形参temp,调用拷贝构造函数,打印"Date(const Date& d):"和temp的地址(这一步在编译器中被优化

④返回temp时,会拷贝一份构造临时对象返回,调用拷贝构造函数,打印"Date(const Date& d):"和临时对象的地址

⑤依次调用递归函数销毁temp(这一步在编译器中被优化),d,临时对象,d1

在这里插入图片描述

传参时,能够进行引用传参的尽量使用引用传参,因为引用传参不需要再拷贝占用空间,提高程序运行效率


今日分享结束~

在这里插入图片描述

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

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

相关文章

Perl语言简介

Perl语言&#xff0c;全称为Practical Extraction and Report Language&#xff08;实用提取与报告语言&#xff09;&#xff0c;是一种高级、通用、解释型的编程语言。它由Larry Wall于1987年首次发布&#xff0c;并迅速因其强大的文本处理能力和高度的灵活性而受到广泛应用。…

数据库安装

1.选择最下面自定义安装 2.选择x64 3.next 4.完成后next 5.next 6.选择如图&#xff0c;next 7.如图 8.输入密码 9.如图 10.如图 11.安装 12.完成 13.控制面板选择系统和安全 14.选择系统 15.高级系统设置 16.环境变量 17.双击打开path 18.新建 19.输入MySQLbin文件夹路径 20.管…

firewalld(4) Rich Rule

简介 前文介绍了firewall基本原理&#xff0c;基础的命令使用、保存、以及zone的配置&#xff0c;前面文章我们在配置zone的时候有些复杂的条件&#xff0c;比如限速、日志记录等并不能直接在zone中进行配置。本篇文章主要介绍richlanguage&#xff0c;它能提供更加丰富的策略配…

分布式日志采集 Loki 配置及部署详细

分布式日志采集 Loki 配置及部署详细 Loki 部署模式Loki 读写分离部署配置Loki 配置大全 Loki 部署模式 &#xff08;1&#xff09;可扩展部署模式 Loki 的简单可扩展部署模式是最简单的部署方式、首选方式。可扩展到每天几TB的日志&#xff0c;但是如果超出这个范围&#xff…

Oracle 11.2.0.1升级到11.2.0.4并做rman备份异机恢复

下载好数据库升级包&#xff0c;想去Oracle官网下载的&#xff0c;提示没有授权 只能在csdn找付费的了&#xff0c;9块1个&#xff0c;下载了前2个。 注意&#xff0c;总共有7个包&#xff0c;如果Oracle是安装在linux服务器&#xff0c;且无图形界面管理的 只需要第一&#xf…

液压传动知识

绪论 工作原理 依靠运动者的液体的压力能传递动力液体在受调节、控制状态下工作&#xff0c;液压传动和控制同等重要液压传动以液体为工作介质 液压系统组成 动力元件&#xff1a;机械能转换为液体压力能。液压泵控制元件&#xff1a;对液体流动方向、压力、流量进行控制或…

PyCharm简介二

PyCharm是一款由JetBrains公司开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专门用于Python开发。它提供了代码编辑、调试、项目管理以及许多其他功能&#xff0c;能够显著提高Python开发者的工作效率。虽然PyCharm不是Python开发的必要软件&#xff0c;但它在以…

uniapp零基础入门Vue3组合式API语法版本开发咸虾米壁纸项目实战

嗨&#xff0c;大家好&#xff0c;我是爱搞知识的咸虾米。 今天给大家带来的是零基础入门uniapp&#xff0c;课程采用的是最新的Vue3组合式API版本&#xff0c;22年发布的uniappVue2版本获得了官方推荐&#xff0c;有很多同学等着我这个vue3版本的那&#xff0c;如果没有学过vu…

php简单的单例模式

本文由 ChatMoney团队出品 单例模式是一种常用的设计模式&#xff0c;它的核心思想是确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取这个实例。在 PHP 中实现单例模式通常有三种形式&#xff1a;饿汉式&#xff08;Eager&#xff09;、懒汉式&#xff08;Lazy&…

部署nginx服务用于浏览服务器目录并实现账号密码认证登录

1、背景&#xff1a; 因公司业务需求&#xff0c;部署一套数据库备份中心服务&#xff0c;但是因为备份的数据库很多&#xff0c;有项目经理要求能经常去查看备份数据库情况及下载备份数据文件的需求。根据该需求&#xff0c;需要在备份数据库的服务器上部署一个nginx服务&…

CentOS 7.9 快速更换 阿里云源教程

CentOS 7.9 更换源教程 总结 # 下载 wget yum -y install wget # 备份 yum 源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak # 下载阿里云的yum源到 /etc/yum.repos.d/ # 此处以 CentOS 7 为例&#xff0c;如果是其它版本或者系统的话&#…

【AIGC自动化编程技巧笔记】一、起步

本专栏参考了CSDN高级讲师李宁的《AIGC自动化编程技巧》&#xff0c;是学习过程中记录的笔记。 一、ChatGPT的实质 尽管ChatGPT的功能非常强大&#xff0c;看似无所不能&#xff0c;但是ChatGPT毕竟只是基于很多算法和 数据并运行在强大GPU上的大量代码而已。ChatGPT甚至并不…

【新奇E100-开发记录】

新奇E100-开发记录 ■ 环境■ 编译器■ Ubuntu主机环境需要安装如下工具■ mips32R1工具链(toolchain )■ defconfig■ dts的选择■ 全局编译■■■ 环境 ■ 编译器 ■ Ubuntu主机环境需要安装如下工具 shell $ sudo apt install git $ sudo apt install gcc $ sudo apt i…

js替换对象里面的对象名称

data为数组&#xff0c;val为修改前的名称&#xff0c;name为修改后的名称 JSON.parse(JSON.stringify(data).replace(/val/g, name)) &#xff1b; 1.替换data里面的对象tenantInfoRespVO名称替换成tenantInfoUpdateReqVO 2.替换语句&#xff1a; 代码可复制 let tenantInf…

基于Spring Boot的先进时尚室内管理系统

1 项目介绍 1.1 研究背景 随着21世纪信息技术革命的到来&#xff0c;互联网的普及与发展对人类社会的演变产生了深远影响&#xff0c;跨越了物质生活的丰盈边界&#xff0c;更深层次地滋养了人类的精神文化生活。在过去&#xff0c;囿于地理位置和技术条件的限制&#xff0c;…

大模型落地过程中的现状和问题——RAG技术方案

大模型有幻觉怎么办&#xff1f;&#xff1f;大模型这么多&#xff0c;怎么选&#xff1f;需要多少硬件资源支持&#xff1f;大模型有幻觉怎么办&#xff1f;&#xff1f;新技术层出不穷&#xff0c;如何跟进&#xff1f;大家都在做长文本对我有什么影响么&#xff1f; 模型微…

第十六章 标准 IP 访问控制列表配置

实验目标 理解标准 IP 访问控制列表的原理及功能&#xff1b; 掌握编号的标准 IP 访问控制列表的配置方法&#xff1b; 实验背景 你是公司的网络管理员&#xff0c;公司的经理部、财务部们和销售部门分属于不同的 3 个网段&#xff0c;三部门之间用路由器进行信息传递&…

码随想录算法训练营第一天| 【数组】704. 二分查找法

704.二分查找 题目 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9…

Python容器 之 练习题

1.字符串的基本使用 # 定义一个字符串 str1, 字符串的内容为 "hello world and itcast and itheima and Python" str1 "hello world and itcast and itheima and Python" # 在字符串str1中查找 字符串 and 的下标 num str1.find(and) print(num) # 12…

直线旋转电机的分类有哪些?有何特点?

直线电机也被称为线性电机&#xff0c;线性马达&#xff0c;直线马达等&#xff0c;它是一种将电能直接转换成直线运动机械能&#xff0c;而不需要任何中间转换机构的传动装置。那么&#xff0c;直线旋转电机的分类有哪些&#xff1f;有何特点&#xff1f;接下来就让小编来为大…