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

类和对象【C++】【中篇】

目录

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

1、构造函数 

2、析构函数

3、拷贝构造函数 

4、赋值重载函数

二、赋值运算符重载 


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

注意:默认成员函数不能在类外面定义成全局函数。因为类里没有的话会自动生成,就会产生冲突。 

1、构造函数 

对于下面这个程序,可以看到,每次创建对象时,我们都需要手动调用Init函数,这样是不是有些麻烦呢?

class Date
{
public:void Init(int year,int month,int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Init(2023, 5, 1);d1.Print();Date d2;d2.Init(2023, 5, 2);d2.Print();return 0;
}

构造函数就可以很好地解决这个问题。

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

其主要任务并不是开空间创建对象,而是初始化对象。

特征:

1、函数名与类名相同。

2、无返回值。(也不需要void)

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;
};
int main()
{Date d1;//调用无参构造函数Date d2(2023, 5, 8);//调用有参的构造函数return 0;
}

注意:

1、如果通过无参构造函数创建对象时,对象后不用跟括号,否则就成了函数声明。

2、如果类中没有显示定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显示定义,编译器将不再生成。

3、编译器自动生成的默认构造函数,对内置类型不做处理(有些编译器可能会),对自定义类型成员则会调用它的默认构造。

一般情况下,有内置类型时,就需要自己写构造函数,如果全是自定义类型成员,就可以考虑让编译器自己生成。

在C++11中针对内置类型成员不初始化的缺陷,又打了补丁,即,内置类型成员变量在类中声明时可以给默认值。

4、无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

2、析构函数

(1)概念

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

(2)特性 

1、析构函数名是在类名前加 ~

2、无参数无返回类型

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

4、析构函数不能重载

5、对象生命周期结束时,C++编译系统自动调用析构函数。

1、一般情况下,有动态申请资源,就需要显示写析构函数释放资源

2、没有动态申请的资源,就不需要写析构

3、如果需要释放的资源的成员类型都是自定义类型,则不需要写析构 

3、拷贝构造函数 

(1)概念

拷贝构造函数:是特殊的成员函数,只有单个形参,该形参是对本类类型对象的引用(一般用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

(2)特征 

1、拷贝构造函数是构造函数的一个重载形式。

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

3、若未显示定义,编译器会生成默认的拷贝构造函数。默认拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

class Date
{
public:Date(int year=1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){//this->_year = d._year;(d2的_year = d1的_year)_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;
}

4、赋值重载函数

d1=d2;//d1.operator =(d2);

已经存在的两个对象之间复制拷贝。

注意:与拷贝构造函数不同的是,拷贝构造函数是用一个对象初始化另一个对象

 下面这个关于日期类的赋值重载函数有没有什么问题?

	void operator =(const Date& d){_year = d._year;_month = d._month;_day = d._day;}
int main()
{Date d1(2023, 5, 8);//带参构造函数Date d2(d1);//拷贝构造函数,用一个已经存在的对象初始化另一个对象Date d3(d2);//拷贝构造函数//将d2赋值给d1,已经存在的两个对象的拷贝复制d1 = d2 = d3;return 0;
}

 在赋值时,如果是连续赋值,上面的函数就会有问题。

d1=d2=d3;

d3赋值给了d2后,应该有一个返回值,而我们上面写的函数的返回值是空。

	Date operator =(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;//传值返回,返回的是对象的拷贝,不好}

如果像上面这样写,是传值返回,就需要再调用拷贝构造函数

因此,为了提高效率,我们可以将返回值改为引用。

	Date& operator =(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;//因为出了作用域*this还在,所以可以用引用返回。}

上面的代码对于d1=d1;这样的代码是可以编译通过的,如果不想,可以按照如下写法:

	Date& operator =(const Date& d){if(*this != &d){_year = d._year;_month = d._month;_day = d._day;} return *this;//因为出了作用域*this还在,所以可以用引用返回。}

默认生成的赋值重载函数跟拷贝构造一样:

1、内置类型成员:值拷贝/浅拷贝。

2、自定义类型成员会去调用它的赋值重载函数。 

二、赋值运算符重载 

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

函数名字:关键字operator后面接需要重载的运算符符号。

函数原型:返回值类型operator操作符(参数列表)

注意:

(1)不能通过其他连接符号来创建新的操作符。

(2)重载操作符必须有一个类类型参数。

(3)用于内置类型的运算符,其含义不能改变,如+

(4)作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this

(5)(.*)(::)(sizeof)(?:)(.)注意以上5个运算符不能重载,笔试选择题中经常出现。

class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//bool operator == (Date* this,const Date& d2)bool operator ==(const Date& d2){return _year == d2._year && _month == d2._month && _day == d2._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1, d2;d1.Init(2023, 5, 1);d2.Init(2023, 5, 2);if (d1 == d2){printf("相等");}else{printf("不相等");}return 0;
}

三、日期类的实现 

#include <iostream>
#include <functional>
#include<cassert>
using namespace std;
class Date
{//友元函数friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);public:Date(int year = 1, int month = 1, int day = 1);//带参构造函数//如果这样写://1、Date d1(2023, 5, 1);//2、const Date d2(2004, 3, 5);//d1.Print(Date* this);//可以//不可以,因为从const Date*this到Date *this权限放大了//d2.Print(Date* this);//void Print()//{//	cout << _year << "-" << _month << "-" << _day << endl;//}//如果将Print的参数改成const Date *this//对于1来说是权限缩小,对于2来说是平移,都是允许的//只要不改变对象成员变量的函数都应该const,这样const对象和普通对象就都可以调用了void Print()const{cout << _year << "-" << _month << "-" << _day << endl;}bool operator <(const Date& x);bool operator >(const Date& x);bool operator ==(const Date& x);bool operator <= (const Date& x);bool operator >= (const Date& x);bool operator !=(const Date& x);int GetMonthDay(int year, int month);Date& operator += (int day);Date operator + (int day);Date& operator++();Date operator++(int);//后置++Date& operator -= (int day);Date& operator - (int day);Date& operator--();Date operator--(int);//后置++int operator-(const Date& x);
private:int _year;int _month;int _day;
};
#include"Date.h"
Date::Date(int year, int month, int day)//带参构造函数
{if (year > 0 && month > 0 && month < 13 && day>0 && day <= GetMonthDay(year, month)){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;assert(false);}
}
bool Date::operator <(const Date& x)
{if (_year < x._year)return true;else if (_year == x._year && _month < x._month)return true;else if (_year == x._year && _month == x._month && _day < x._day)return true;else return false;
}
bool Date :: operator ==(const Date& x)
{return _year == x._year && _month == x._month && _day == x._day;
}
bool Date:: operator <= (const Date& x)
{return *this < x || *this == x;
}
bool Date :: operator >(const Date& x)
{return !(*this <= x);
}
bool Date :: operator >= (const Date& x)
{return *this > x || *this == x;
}
bool Date :: operator!=(const Date& x)
{return !(*this == x);
}int Date::GetMonthDay(int year, int month)
{int daysArr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (month==2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)){return 29;}return daysArr[month];
}
Date& Date :: operator+=(int day)
{if (_day < 0){return *this -= (-day);}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){++_year;_month = 1;}}return *this;
}
Date Date:: operator+(int day)
{Date tmp(*this);tmp += day;return tmp;
}
Date& Date :: operator++()//前置++
{*this += 1;return *this;
}
Date Date::operator++(int)//后置++
{Date tmp(*this);*this += 1;return tmp;
}
Date& Date :: operator -= (int day)
{if (_day < 0){return *this += -day;}_day -= day;while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}Date& Date:: operator - (int day)
{Date tmp(*this);tmp -= day;return tmp;
}Date& Date:: operator--()
{*this -= 1;return *this;
}
Date Date:: operator--(int)//后置++
{Date tmp(*this);*this -= 1;return tmp;
}
int Date:: operator-(const Date& x)
{Date max = *this;Date min = x;int flag = 1;if (*this<x){max = x;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}
//void Date:: operator <<(ostream& out)
//{
//	out << _year << "年" << _month << "月" << _day << "日" << endl;
//}ostream& operator <<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}
istream& operator>>(istream& in, Date& d)
{//写个检查in >> d._year >> d._month >> d._day;return in;
}

相关文章:

类和对象【C++】【中篇】

目录 一、类的6个默认成员函数 1、构造函数 2、析构函数 3、拷贝构造函数 4、赋值重载函数 二、赋值运算符重载 一、类的6个默认成员函数 注意&#xff1a;默认成员函数不能在类外面定义成全局函数。因为类里没有的话会自动生成&#xff0c;就会产生冲突。 1、构造函数…...

2.SpringBoot运维实用篇

SpringBoot运维实用篇 ​ 基础篇发布以后&#xff0c;看到了很多小伙伴在网上的留言&#xff0c;也帮助超过100位小伙伴解决了一些遇到的问题&#xff0c;并且已经发现了部分问题具有典型性&#xff0c;预计将有些问题在后面篇章的合适位置添加到本套课程中&#xff0c;作为解…...

【c++】浅讲引用

【c】浅讲引用 前言引用定义作用做输出型参数引用作返回值总结 关于引用的权限 结尾 前言 博主开始细学c和linux了 这次就带来浅学了的引用。 引用 定义 引用不是在内存中开辟一个新空间的新变量 类似于给变量取别名&#xff0c;和取别名的对象在空间中公用一个对象 例&#…...

CSS布局基础(文字[行内<块>]与行内[块]垂直对齐方式 文字溢出显示省略号)

文字[行内<块>]与行内[块]垂直对齐方式 文字[行内<块>]与行内[块]垂直对齐方式概述图片底部空隙问题 文字溢出显示省略号单行文字多行文字 文字[行内<块>]与行内[块]垂直对齐方式 概述 vertical-align: top | middle| bottom | baseline(默认) | sub | sup…...

AI自动写文章_免费在线原创文章生成器

自动写文章生成器 自动写文章生成器是一种利用人工智能和自然语言处理技术&#xff0c;帮助用户快速生成文章的工具。该软件可以根据用户的需求和选择&#xff0c;自动生成符合要求的文章&#xff0c;无需手动编写和修改。 自动写文章生成器的主要功能包括以下几个方面&#…...

Java阶段二Day15

Java阶段二Day15 文章目录 Java阶段二Day15复习前日知识点对象数据类型注入数组类型注入集合类型的注入p命名空间引入外部属性文件 基于XML管理beanbean的作用域bean的生命周期代码演示生命周期后置处理器处理展示基于XML的自动装配 基于注解管理bean开启组件扫描使用注解定义B…...

从月薪3000到月薪20000,自动化测试应该这样学...

绝大多数测试工程师都是从功能测试做起的&#xff0c;工作忙忙碌碌&#xff0c;每天在各种业务需求学习和点点中度过&#xff0c;过了好多年发现自己还只是一个功能测试工程师。 随着移动互联网的发展&#xff0c;从业人员能力的整体进步&#xff0c;软件测试需要具备的能力要…...

Python魔法方法 单例模式

前言 本文介绍一下python中常用的魔法方法以及面向对象中非常重要的单例模式。 魔法方法 python中一切皆对象&#xff0c;因为python是面向对象的编程语言。python给类和对象提供了大量的内置方法&#xff0c;这些内置方法也称魔法方法。这些魔法方法总是在某种条件下自动触…...

计算机网络基础知识(三)—— 什么是OSI七层模型?

文章目录 00 | &#x1f6f8;发展史&#x1f6f8;01 | &#x1f6f8;OSI七层参考模型&#x1f6f8;02 | &#x1f6f8;OSI七层参考模型的信息流向&#x1f6f8; OSI七层模型是Open Systems Interconnection Reference Model的缩写&#xff0c;是由国际标准化组织&#xff08;IS…...

Python(符号计算常微分方程)谐振子牛顿运动方程

牛顿运动方程 牛顿运动方程可以写成以下形式 F d p d t m d v d t m d 2 r d t 2 \mathbf{F}\frac{d \mathbf{p}}{d t}m \frac{d \mathbf{v}}{d t}m \frac{d^2 \mathbf{r}}{d t^2} Fdtdp​mdtdv​mdt2d2r​ 恒力问题 具有恒定力的问题意味着恒定的加速度。 典型的例子是…...

OpenCL编程指南-1.2OpenCL基本概念

OpenCL概念基础 面向异构平台的应用都必须完成以下步骤&#xff1a; 1&#xff09;发现构成异构系统的组件。 2&#xff09;探查这些组件的特征&#xff0c;使软件能够适应不同硬件单元的特定特性。 3&#xff09;创建将在平台上运行的指令块&#xff08;内核)。 4&#xff09…...

使用 ChatGPT 辅助学习——为自己找一个老师

我们每个人都有许多标签&#xff0c;例如高中生、成绩中等、文科&#xff0c;根据这些标签我和其他拥有相同标签的人分配了相同的教程、班级和老师&#xff0c;这可以带来效率上的提升&#xff0c;因为同一份教程、老师就可以服务几十上百人&#xff0c;而无须为每个人定制&…...

MySQL基础(二十一)用户与权限管理

1. 用户管理 1.1 登录MySQL服务器 启动MySQL服务后&#xff0c;可以通过mysql命令来登录MySQL服务器&#xff0c;命令如下&#xff1a; mysql –h hostname|hostIP –P port –u username –p DatabaseName –e "SQL语句"-h参数后面接主机名或者主机IP&#xff0c…...

程序员的下一个风口

面对近一年的裁员潮&#xff0c;以及 GPT 出现带来的 AI 颠覆潮流&#xff0c;各种话题出现&#xff1a;「前端已死」、「后端已死」、「Copy/Paste 程序员将被 AI 取代」。程序员行业是否还有发展空间&#xff1f; 这一两年的就业机会是因为经济衰落周期内造成的&#xff0c;不…...

Android 自定义View 之 简易输入框

简易输入框 前言正文① 构造方法② XML样式③ 测量④ 绘制1. 绘制方框2. 绘制文字 ⑤ 输入1. 键盘布局2. 键盘接口3. 键盘弹窗4. 显示键盘5. 相关API 四、使用自定义View五、源码 前言 在日常工作开发中&#xff0c;我们时长会遇到各种各样的需求&#xff0c;不部分需求是可以通…...

SpringMVC的基础知识

创建SpringMVC项目 SpringMVC项目其实和SpingBoot项目差不多,就多引入了一个SpringWeb项目而已拉 可以看这篇博客,创建的就是一个SpringMVC项目--创建项目の博客 SpringMVC是啥 Spring是啥相信大家都了解 啥是MVC呢?MVC是Model View Controller的缩写 我们分开看这三个词Model…...

OpenPCDet系列 | 4.2 DataAugmentor点云数据增强模块解析

文章目录 DataAugmentor模块解析1. gt_sampling2. random_world_flip3. random_world_rotation4. random_world_scaling5. limit_period DataAugmentor模块解析 在pointpillars算法中&#xff0c;具体的数据增强方法配置是在yaml中的DATA_CONFIG.DATA_AUGMENTOR进行配置&#…...

精准测试之过程与实践 | 京东云技术团队

作者&#xff1a;京东工业 宛煜昕 一、怎样的技术 •百度百科&#xff1a; 精准测试是一套计算机测试辅助分析系统。 精准测试的核心组件包含的软件测试示波器、用例和代码的双向追溯、智能回归测试用例选取、覆盖率分析、缺陷定位、测试用例聚类分析、测试用例自动生成系统…...

类ChatGPT逐行代码解读(1/2):从零实现Transformer、ChatGLM-6B

前言 最近一直在做类ChatGPT项目的部署 微调&#xff0c;关注比较多的是两个&#xff1a;一个LLaMA&#xff0c;一个ChatGLM&#xff0c;会发现有不少模型是基于这两个模型去做微调的&#xff0c;说到微调&#xff0c;那具体怎么微调呢&#xff0c;因此又详细了解了一下微调代…...

车道线检测

前言 目前&#xff0c;车道线检测技术已经相当成熟&#xff0c;主要应用在自动驾驶、智能交通等领域。下面列举一些当下最流行的车道线检测方法&#xff1a; 基于图像处理的车道线检测方法。该方法是通过图像处理技术从摄像头传回的图像中提取车道线信息的一种方法&#xff0c…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...

Go 语言中的内置运算符

1. 算术运算符 注意&#xff1a; &#xff08;自增&#xff09;和--&#xff08;自减&#xff09;在 Go 语言中是单独的语句&#xff0c;并不是运算符。 package mainimport "fmt"func main() {fmt.Println("103", 103) // 13fmt.Println("10-3…...

自动化立体仓库堆垛机控制系统STEP7 OB1功能块

1、堆垛机控制系统STEP7硬件组态如下图 CPU CPU 314C-2 PN/DP 6ES7 314-6EH04-0AB0 SM 338 POS-INPUT AO2x12Bit 6ES7 332-5HB01-0AB0 2、堆垛机控制系统STEP7内部变量 前进HMI M 0.0 BOOL 后退HMI M 0.1 BOOL 上升HMI M 0.2 B…...

MySQL-运维篇

运维篇 日志 错误日志 错误日志是 MySQL 中最重要的日志之一&#xff0c;它记录了当 mysqld 启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息当数据库出现任何故障导致无法正常使用时&#xff0c;建议首先查看此日志。 该日志是默认开启的&am…...