类与对象(中)

类与对象(中)

  • 一、类的六个默认成员函数
  • 二、构造函数
    • 1、概念
    • 2、代码
    • 3、特点
    • 4、编译器生成的默认构造函数的作用
      • (1)内置类型(基本类型)和自定义类型的概念
      • (2)作用
      • (3)代码
      • (4)运行结果
  • 三、析构函数
    • 1、概念
    • 2、特点
    • 3、代码
  • 四、拷贝构造函数
    • 1、概念
    • 2、函数形式
    • 3、简单使用
    • 4、运行结果
    • 5、特点
    • 6、拷贝构造函数使用传值方式
      • (1)代码
      • (2)图解
    • 7、拷贝构造函数使用浅拷贝引发的错误
      • (1)代码
      • (2)错误原因
    • 8、使用拷贝构造函数的场景
  • 五、运算符重载
    • 1、概念
    • 2、注意
    • 3、赋值运算符重载
      • (1)重载格式
      • (2)代码
    • 4、运算符重载和函数重载
  • 六、const成员
    • 1、概念
    • 2、代码
    • 3、运行结果

本文是接续文章类与对象(上)的。

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

如果一个类中什么成员都没有,则称这个类为空类,但空类中并不是什么都没有,而是存在六个默认成员函数(用户没有显式实现,编译器会生成的成员函数)。即任何类在什么都不写的情况下,编译器会自动生成六个默认成员函数。
在这里插入图片描述

  • 构造函数:主要完成初始化工作。
  • 析构函数:主要完成清理工作。
  • 拷贝构造:使用同类对象初始化创建一个新的对象。
  • 赋值重载:主要是把一个对象赋值给另一个对象。
  • 两个取地址重载:主要是普通对象和const对象取地址,这两个很少会自己实现,因为编译器默认生成的就够用了。

二、构造函数

1、概念

构造函数是一个特殊的成员函数,它的函数名与类名相同,当创建类类型对象时,编译器会自动调用这个函数,以保证每个数据成员都有一个合适的初始值,并且在对象的整个生命周期内只调用一次。

2、代码

class Date
{
public://Date()			//无参构造函数//{}Date(int year = 2023, int month = 8, int day = 17)			//全缺省构造函数{_year = year;_month = month;_day = day;}
private:int _year = 1;int _month = 1;int _day = 1;
};

3、特点

  • 构造函数虽然有构造两个字,但是构造函数的主要任务并不是开辟空间创建对象,而是初始化对象。
  • 构造函数的函数名与类名相同、无返回值类型、对象实例化时编译器自动调用对应的构造函数、可以重载。
  • 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数;如果用户显式定义了构造函数,则编译器将不再生成。
  • C++11中针对内置类型成员不初始化的缺陷打了一个补丁,即内置类型成员变量在类中声明时可以给默认值(缺省值)。如上方代码中private修饰的成员变量。
  • 无参构造函数、全缺省构造函数、我们没显式写编译器默认生成的构造函数,都可以认为是默认构造函数(不用传参数就可以调用的),但类中的默认构造函数只能有一个。

4、编译器生成的默认构造函数的作用

(1)内置类型(基本类型)和自定义类型的概念

内置类型:语言提供的数据类型,如:int、char、double等等。
自定义类型:我们使用class、struct、union等等,自己定义的类型。

(2)作用

编译器生成的默认构造函数在进行初始化时,对内置类型不做处理,而对自定义类型会去调用它(这个自定义类型)的默认构造函数。

(3)代码

class Time
{
public:Time(){_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};class Date
{
public:
private://内置类型(基本类型)int _year;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}

(4)运行结果

在这里插入图片描述

三、析构函数

1、概念

析构函数与构造函数的功能相反,析构函数不是完成对对象本身的销毁,因为局部对象销毁工作是由编译器完成的,而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

2、特点

  • 析构函数的函数名是在类名前加上字符~,它无参数且无返回值类型。
  • 一个类中只能有一个析构函数且不能重载。若未显式定义,系统会自动生成默认的析构函数。
  • 对象生命周期结束时,C++编译系统系统会自动调用析构函数。
  • 编译器生成的默认析构函数与构造函数一样,内置类型不做处理,自定义类型会去调用它的析构函数。
  • 如果类中没有申请资源(在堆上开辟空间),这个类的析构函数可以不写,编译器生成的默认析构函数就够用了,比如Date类;但如果类中有资源申请时,一定要写,否则会造成资源泄漏(内存泄漏),比如Stack类(参见栈与队列)。

3、代码

typedef int DataType;
class Stack
{
public://构造函数Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:DataType* _array;int _capacity;int _size;
};class MyQueue
{
public:
private:Stack st1;Stack st2;
};int main()
{MyQueue mq;return 0;
}
  • MyQueue类中有两个Stack类的自定义类型,MyQueue类中可以不用析构函数,但Stack类需要析构函数,否则会造成内存泄漏。

四、拷贝构造函数

1、概念

在创建对象时,创建一个与已存在对象一某一样的新对象。

2、函数形式

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

3、简单使用

class Date
{
public:Date(int year = 2023, int month = 8, int day = 17){_year = year;_month = month;_day = day;cout << "Date(int year = 2023, int month = 8, int day = 17)" << endl;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;cout << "Date(const Date& d):" << &d << endl;}~Date(){cout << "~Date():" << this << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2023, 8, 19);Date d2(d1);return 0;
}

4、运行结果

在这里插入图片描述

5、特点

  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式时,编译器会报错,因为会引发无穷递归调用。
  • 若未显式定义,编译器会生成默认的拷贝构造函数。当使用拷贝构造函数创建对象时会调用这个拷贝构造函数,而它会按照字节序的方式完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。而自定义类型则会调用其(自定义类型)拷贝构造函数完成拷贝。
  • 类中如果没有涉及资源申请(在堆上开辟空间)时,拷贝构造函数有没有写都无所谓;但如果涉及到资源申请时,则拷贝构造函数一定要写且需写为深拷贝,否则就是浅拷贝,程序会出问题。

6、拷贝构造函数使用传值方式

(1)代码

class Date
{
public:Date(int year = 2023, int month = 8, int day = 17){_year = year;_month = month;_day = day;cout << "Date(int year = 2023, int month = 8, int day = 17)" << endl;}Date(const Date d){_year = d._year;_month = d._month;_day = d._day;cout << "Date(const Date& d):" << &d << endl;}~Date(){cout << "~Date():" << this << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2023, 8, 19);Date d2(d1);return 0;
}

(2)图解

在这里插入图片描述

7、拷贝构造函数使用浅拷贝引发的错误

(1)代码

int main()
{Stack st1;st1.Push(1);st1.Push(2);st1.Push(3);st1.Push(4);Stack st2(st1);return 0;
}
  • Stack类参见上方析构函数代码。

(2)错误原因

  • 因为类中没有显式的拷贝构造函数,所以编译器会生成一个默认的拷贝构造函数。
  • 当使用拷贝构造函数创建对象时,会按照字节序的方式完成拷贝,即浅拷贝。此时,使用拷贝构造函数生成的对象(st2)与已存在且作为拷贝构造函数参数的对象(st1),它们的_array将指向同一块空间,即对象st1的_array指向的空间。
  • 当函数结束时,编译器会自动调用它们的析构函数,当st1对象析构完时是不会报错的,但此时st1的_array指向的空间已经被释放了,而st2也要调用它的析构函数,它的_array也要进行释放,这会导致_array指向的空间被释放两次而导致程序崩溃。

8、使用拷贝构造函数的场景

  • 使用已存在的对象创建新对象。
  • 函数参数类型为类类型对象。
  • 函数返回值类型为类类型对象。

五、运算符重载

1、概念

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,它具有返回值类型,函数名以及参数列表,其返回值类型和参数列表与普通的函数类似。

  • 运算符重载的函数名:关键字operator后面接需要重载的运算符符号。
  • 运算符重载的函数原型:返回值类型 operator操作符(参数列表)。

2、注意

  • 不能通过连接其他符号来创建新的操作符:比如operator@ 。
  • 重载操作符必须有一个类类型参数。
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义。
  • 作为类成员函数重载时,其形参列表的形参会比操作数数量少一个,因为成员函数的第一个参数为隐藏的this。
  • .* :: sizeof ?: . 这5个运算符不能重载。

3、赋值运算符重载

(1)重载格式

形式参数类型:const 类类型&,形式参数使用引用可以提高传参的效率。
返回值类型:类类型&,返回值类型使用引用可以提高返回的效率,有返回值的目的是为了支持连续赋值。

(2)代码

//Date.h
class Date
{
public:// 全缺省的构造函数Date(int year = 2023, int month = 8, int day = 20){if (month < 1 || month > 12|| day < 1 || day > GetMonthDay(year, month)){cout << "非法日期" << endl;}_year = year;_month = month;_day = day;}Date& operator=(const Date& d);
private:int _year;int _month;int _day;
};//Date.cpp
Date& Date::operator=(const Date& d)
{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}//赋值运算符重载测试代码
int main()
{Date d3(2023, 8, 21);Date d4, d5;d5 = d4 = d3;return 0;
}
  • Date类中的全缺省的构造函数中可以加上里面的if判断语句,当输入的日期是错误的时,会输出错误信息而不是继续进行。
  • 赋值运算符重载实现的代码中加上if判断语句,当进行赋值的两个元素是同一个对象时不进行任何操作,因为无意义。
  • 赋值运算符重载实现的代码中返回*this是要达成连续赋值的要求。
  • 赋值运算符只能重载成类的成员函数而不能重载成全局函数,因为重载成全局函数时就没有this指针了,需要给两个参数,而这样会使使用赋值运算符时变得麻烦;再者,赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就会和编译器在类中生成的默认赋值运算符重载起冲突。
  • 当用户没有显式实现赋值运算符时,编译器会生成一个默认的赋值运算符重载,以值的方式逐字节拷贝。此时,内置类型成员变量是直接进行赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
  • 如果类中没有涉及到资源管理(在堆上开辟空间),则这个类的赋值运算符是否显式实现都无所谓;但当涉及到资源管理时,则必须要显式实现且需为深赋值,否则会出现一些问题。

4、运算符重载和函数重载

  • 运算符重载:让自定义类型对象可以使用运算符。当使用运算符时转换成调用这个重载函数。
  • 函数重载:支持函数名相同的函数同时存在。
  • 运算符重载和函数重载虽然都有重载这个词,但是他们之间没有必然的联系。

六、const成员

1、概念

被const修饰的“成员函数”称之为const成员函数,当const修饰类成员函数时,实际修饰的是该成员函数形参列表中隐含的this指针形参,表明在该成员函数中不能对this指向的类的任何成员进行修改。

2、代码

#include<iostream>
using namespace std;class Date
{
public:Date(int year = 2023, int month = 8, int day = 22){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << _year << " " << _month << " " << _day << endl;}//实际的参数:void Print(const Date* const this)void Print() const{cout << "Print()const" << endl;cout << _year << " " << _month << " " << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;const Date d2;d1.Print();d2.Print();return 0;
}

3、运行结果

在这里插入图片描述

  • 使用const修饰成员函数时,修饰的是this指向的对象,保证了该成员函数内部不会修改this指向的对象的成员变量,此时const对象和非const对象都可以调用这个成员函数。
  • 没有被const修饰的对象是可以调用const修饰的成员函数的,只是对象的权限缩小了,从可以修改变成不能修改。
  • 被const修饰的对象不能调用没有被const修饰的成员函数,因为对象的权限放大了,即从不可以修改变成可以修改。

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请务必点赞、收藏加关注💕💕💕

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

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

相关文章

(WAF)Web应用程序防火墙介绍

&#xff08;WAF&#xff09;Web应用程序防火墙介绍 1. WAF概述 ​ Web应用程序防火墙&#xff08;WAF&#xff09;是一种关键的网络安全解决方案&#xff0c;用于保护Web应用程序免受各种网络攻击和威胁。随着互联网的不断发展&#xff0c;Web应用程序变得越来越复杂&#x…

2021年03月 C/C++(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;酒鬼 Santo刚刚与房东打赌赢得了一间在New Clondike 的大客厅。今天&#xff0c;他来到这个大客厅欣赏他的奖品。房东摆出了一行瓶子在酒吧上。瓶子里都装有不同体积的酒。令Santo高兴的是&#xff0c;瓶子中的酒都有不同的味道。房东说道&#xff1a;“你可以…

机器学习深度学习——针对序列级和词元级应用微调BERT

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——NLP实战&#xff08;自然语言推断——注意力机制实现&#xff09; &#x1f4da;订阅专栏&#xff1a;机…

游戏开发服务器选型的横向对比

来源一个某乎的作者&#xff0c;貌似来自台湾 上篇介绍了go版本的游戏服务器&#xff0c;这篇介绍下其它语言版本&#xff1a; SkynetkbengineNoahGameFramePomeloPinusET使用的语言C/LuaCCNodejsTypeScriptC#概述云风前辈开源的框架mmo框架server一个快速的、可扩展的、分布…

win11出现安全中心空白和IT管理员已限制对此应用的某些区域的访问

问题 windows安全中心服务被禁用 winr 输入services.msc 找到windows安全中心服务查看是否被禁用&#xff0c;改为启动&#xff0c;不可以改动看第三条 打开设置&#xff0c;找到应用—windows安全中心–终止–修复–重置 重启如果还是不行看第四条 家庭版系统需要打开gped…

我是怎么从0到1搭建性能门禁系统的

背景 页面的性能对于用户的体验起着至关重要的作用&#xff0c;根据Mobify 研究发现&#xff0c;首页加载时间每减少100 毫秒&#xff0c;用户留存率就会增加1.11%。所以做好页面的性能优化&#xff0c;对于网站来说是一个非常重要的步骤。 在解决问题之前需要度量问题&#x…

期权分仓开户资金是否安全?具体保障措施有哪些?

网上关于期权分仓系统的真假一直都没有定论&#xff0c;两方人的争论也让很多没有接触过期权分仓系统的人摸不着头脑&#xff0c;那么期权分仓靠谱吗&#xff1f;资金在里面安全吗&#xff1f;下文为大家科普期权分仓开户资金是否安全?具体保障措施有哪些&#xff1f; 一、期权…

arm:day9

1。思维导图 2..I2C实验&#xff0c;检测温度和湿度 iic.h #ifndef __IIC_H__ #define __IIC_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" #include "gpio.h" /* 通过程序模拟实现I2C总线的时序和协议* GPIOF ---> AHB4…

指针(初阶)

1. 指针是什么&#xff1f; 指针是什么&#xff1f; 指针理解的2个要点&#xff1a; 1. 指针是内存中一个最小单元的编号&#xff0c;也就是地址 2. 平时口语中说的指针&#xff0c;通常指的是指针变量&#xff0c;是用来存放内存地址的变量 总结&#xff1a;指针就是地址&…

K8s中的Namespace是什么?

如何理解Namespace默认的Namespace使用Namespace的好处创建和使用Namespace使用命令行创建使用YAML文件创建Namespace 用例切换Namespace删除Namespace 感谢 &#x1f496; hello大家好&#x1f60a; 由于能够无缝管理和扩展工作负载&#xff0c;Kubernetes &#xff08;简称K8…

【图论】拓扑排序

一.定义 拓扑排序是一种对有向无环图&#xff08;DAG&#xff09;进行排序的算法&#xff0c;使得图中的每个顶点在排序中都位于其依赖的顶点之后。它通常用于表示一些任务之间的依赖关系&#xff0c;例如在一个项目中&#xff0c;某些任务必须在其他任务之前完成。 拓扑排序的…

Flutter对象状态动态监听Watcher

场景&#xff1a;当一个表单需要在表单全部或者特定项赋值后才会让提交按钮可点击。 1.普通实现方式&#xff1a; ///场景&#xff1a;检查[test11][test12][test13]均不为空时做一些事情&#xff0c;例如提交按钮变成可点击String? test11;String? test12;int? test13;///当…

rust库学习-env_logger(actix-web添加彩色日志、rust添加彩色日志 )

文章目录 介绍actix-web启用彩色日志crate地址&json格式日志 我们在进行rust的web开发时&#xff0c;如果不指定日志&#xff0c;就不会有输出&#xff0c;非常不友好 这里我们使用env_logger进行日志打印 介绍 env_logger 需要配合 log 库使用, env_logger 是 Rust 社区…

Docker基本部署和相关操作

1.安装docker服务&#xff0c;配置镜像加速器 1、yum安装并且添加源信息 yum install yum-utils device-mapper-persistent-data lvm2 -y yum-config-manager --add-repo https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo2、修改一些配置信息 sed…

视频监控平台EasyCVR视频汇聚平台档案库房图书馆等可视化管理平台应用场景全面解析

档案是一种特殊的记录留存文献&#xff0c;具有珍贵的精神财富价值。它们是人类活动的真实见证&#xff0c;是辉煌时刻的历史记录&#xff0c;在社会发展和经济建设中发挥着至关重要的作用。 随着市场经济的不断发展和人类文明的飞速推进&#xff0c;档案的价值和作用变得越来…

C#实现简单TCP服务器和客户端网络编程

在C#中进行网络编程涉及许多类和命名空间&#xff0c;用于创建和管理网络连接、传输数据等。下面是一些主要涉及的类和命名空间&#xff1a; System.Net 命名空间&#xff1a;这个命名空间提供了大部分网络编程所需的类&#xff0c;包括&#xff1a; IPAddress&#xff1a;用于…

数据结构(6)

2-3查找树 2-结点&#xff1a;含有一个键(及其对应的值)和两条链&#xff0c;左链接指向2-3树中的键都小于该结点&#xff0c;右链接指向的2-3树中的键都大于该结点。 3-结点&#xff1a;含有两个键(及其对应的值)和三条链&#xff0c;左链接指向的2-3树中的键都小于该结点&a…

什么是JVM ?

目录 一、JVM 简介 1.1 JVM 发展史 1.Sun Classic VM 2.Exact VM 3.HotSpot VM 4.JRockit 5.J9 JVM 6.Taobao JVM&#xff08;国产研发&#xff09; 1.2 JVM 和《Java虚拟机规范》 二、 JVM 运行流程 JVM 执行流程 三、JVM 运行时数据区 3.1 堆&#xff08;线程共享…

【TypeScript】声明文件

在 TypeScript 中&#xff0c;声明文件&#xff08;Declaration Files&#xff09;用于描述已有 JavaScript 代码库的类型信息&#xff0c;以便在 TypeScript 项目中使用这些代码库时获得类型支持。 当你在 TypeScript 项目中引用外部 JavaScript 模块或库时&#xff0c;可能会…

【开发】tips:视频汇聚/视频云存储/视频监控管理平台EasyCVR如何提升网络稳定

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…