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

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


类和对象

  • 五、赋值运算符重载
    • 1、运算符重载
    • 2、赋值运算符重载
    • 3、前置++和后置++重载
  • 六、const成员
  • 七、日期类的实现
    • Date.h
    • Date.cpp
    • test.cpp
      • test1测试结果
      • test2测试结果
      • test3测试结果
      • test4测试结果
      • test5测试结果
      • test6测试结果
      • test7测试结果
  • 八、关于取地址和const取地址操作符重载

五、赋值运算符重载

1、运算符重载

运算符重载是具有特殊函数名的函数,是C++为了增强代码可读性而引入的

operator sign(parameter);

operator为关键字,sign就是需要重载的运算符符号,parameter为参数(可以为多个)

注意事项:

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

重载操作符至少有一个类类型参数

用于内置类型之间的运算符含义不改变,编译器会自动检测使用运算符的类型是什么,从而选择是否改变运算符含义

类成员函数重载时有一个隐藏的参数this

不能重载的五个运算符
.*
::
sizeof
?:
.

重载一个==

//……全局
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}
//……

但定义一个全局函数需要成员函数公有,所以我们直接定义在类内,保证其封装性

//……类内
bool operator==(const Date& d2)
{return _year == d2._year&& _month == d2._month&& _day == d2._day;
}
//……

2、赋值运算符重载

(1)格式
参数类型 const name& 引用传参

返回值类型 name& 返回引用

检测是否自己给自己赋值

返回*this

(2)赋值运算符的重载
赋值运算符只能重载成类的成员函数不能重载成全局函数

赋值运算符如果不显式实现,编译器会生成一个默认的,此时再在外边实现一个全局的赋值运算符重载,就会发生冲突

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}Date(const Date & d){ _year = d._year;_month = d._month;_day = d._day;}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}private:int _year;int _month;int _day;
};

(3)用户没有显式实现时,编译器会生成一个默认赋值运算符重载,然后值拷贝,内置成员直接赋值,自定义成员需要调用对应类的赋值运算符重载完成赋值

(4)有了值拷贝我们就一定要说说深拷贝,在Date类这样的类中不需要我们自己实现,而在Stack这样的类中就需要显式实现,进行资源管理

拿出我们的老演员栈:

typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){arr = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == arr){perror("malloc申请空间失败");return;}size = 0;capacity = capacity;}void Push(const DataType& data){// CheckCapacity();arr[size] = data;size++;}~Stack(){if (arr){free(arr);arr = nullptr;capacity = 0;size = 0;}}
private:DataType* arr;size_t size;size_t capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);Stack s2;s2 = s1;return 0;
}

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

3、前置++和后置++重载

我们先来复习一下前置++和后置++的区别,在仅自加时也就是在n++为一条语句时没有区别,在赋值时,前置++是先+1后赋值,后置++是先赋值再+1

如果我们想要++重载,那么就是定义operator++,分不出为前置还是后置,所以我们规定operator++为前置++operator++(int)为后置++

class Date
{
public:Date(int year = 1970, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date& operator++(){*this += 1;return *this;}Date operator++(int){Date tmp(*this);*this += 1;return tmp;}//所以后置++会使用空间拷贝,效率比前置++低void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d;Date d1(2000,1,1);d = d1++;d.Print();d1.Print();d = ++d1;d.Print();d1.Print();return 0;
}

在这里插入图片描述

六、const成员

被const修饰的成员函数称之为const成员函数,const实际修饰其中隐含的this指针,表明在该成员函数中不能对类内的任何成员进行修改

因为参数为隐藏的,所以我们的方法如下:

void Date::Print() const
{cout << _year << "/" << _month << "/" << _day << endl;
}

相当于

void Date::Print(const Date* this) 
{cout << _year << "/" << _month << "/" << _day << endl;
}

注意:

const对象不能调用非const成员函数

非const对象能调用const成员函数

const成员函数内不能调用其他非const成员函数

非const成员函数内能调用其他const成员函数

七、日期类的实现

Date.h

#pragma once
#include <iostream>
using namespace std;class Date
{
public:int GetMonthDay(int year, int month);Date(int year = 1970, int month = 1, int day = 1);Date(const Date& d);~Date();Date& operator=(const Date& d);Date& operator+=(int day);Date& operator-=(int day);Date& operator++();Date operator++(int);Date& operator--();Date operator--(int);Date operator+(int day);Date operator-(int day);bool operator==(const Date& d);bool operator<(const Date& d);bool operator<=(const Date& d);bool operator>(const Date& d);bool operator>=(const Date& d);bool operator!=(const Date& d);int operator-(const Date& d);void Print() const;
private:int _year;int _month;int _day;
};

Date.cpp

#define _CRT_SECURE_NO_WARNINGS#include "Date.h"int Date::GetMonthDay(int year, int month)
{const static int days[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 days[month];
}Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;if (month < 1 || month > 12|| day < 1 || day > GetMonthDay(year, month)){cout << "非法日期" << endl;exit(-1);}
}Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}Date::~Date()
{_year = _month = _day = 0;
}
//日期类的析构函数其实没必要写
Date& Date::operator=(const Date& d)
{if(this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}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)
{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++()
{*this += 1;return *this;
}Date Date::operator++(int)
{Date tmp(*this);*this += 1;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)
{Date tmp(*this);tmp += day;return tmp;
}Date Date::operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}bool Date::operator<(const Date& d)
{if (_year < d._year){return true;}else if (_year == d._year && _month < d._month){return true;}else if (_year == d._year && _month == d._month && _day < d._day){return true;}else{return false;}
}// d1 <= d2
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;
}bool Date::operator>(const Date& d)
{return !(*this <= d);
}bool Date::operator>=(const Date& d)
{return !(*this < d);
}bool Date::operator!=(const Date& d)
{return !(*this == d);
}int Date::operator-(const Date& d)
{
//假设左大右小,此时标识符flag为1Date max = *this;Date min = d;int flag = 1;
//如果左小右大,则置换后置标识符flag为-1if (*this < d){max = d;min = *this;flag = -1;}
//算出大的与小的之间的天数int n = 0;while (min != max){++min;++n;}
//返回与标识符flag的乘积return n * flag;
}void Date::Print() const
{cout << _year << "/" << _month << "/" << _day << endl;
}

test.cpp

#define _CRT_SECURE_NO_WARNINGS#include "Date.h"
//初始化
void test1()
{Date d1(2000, 1, 1);d1.Print();Date d2;d2.Print();Date d3 = Date(d1);d3.Print();
}
//++
void test2()
{Date d1(2001, 2, 28);Date d2 = d1++;Date d3 = ++d1;
}
//+ -
void test3()
{Date d1(2000, 1, 1);Date d2 = d1 + 20000;Date d3 = d1 - 20000;
}
//--
void test4()
{Date d1(2000, 3, 1);Date d2 = d1--;Date d3 = --d1;
}
//+= -=
void test5()
{Date d1(2000, 1, 1);d1 += 20000;Date d2;d2 -= 20000;
}
//日期-日期
void test6()
{Date d1(2000, 1, 1);Date d2;int a = d1 - d2;
}
//=
void test7()
{Date d1(2000, 1, 1);Date d2;d2 = d1;
}
int main()
{//test1();//test2();//test3();//test4();//test5();//test6();//test7();return 0;
}

test1测试结果

在这里插入图片描述
构造和拷贝构造函数正常

test2测试结果

在这里插入图片描述
在这里插入图片描述
前置后置++都正常

test3测试结果

在这里插入图片描述

在这里插入图片描述

+、 - 不改变原来的值,正常

test4测试结果

在这里插入图片描述

在这里插入图片描述
前置后置- -正常

test5测试结果

在这里插入图片描述
-= +=改变原来的数,正常

test6测试结果

在这里插入图片描述
日期减日期为整数正常

test7测试结果

在这里插入图片描述

=赋值正常

全部正常

八、关于取地址和const取地址操作符重载

&
const …… &
这两个一般不用重新定义,编译器会默认生成,如果有别的用途,可以显式定义重载


今日分享结束~

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

基于Java+SpringMvc+Vue技术智慧校园系统设计与实现--60页及以上论文参考

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…

8、Redis 的线程模型、I/O 模型和多线程

Redis 的线程模型、I/O 模型和多线程 1. Redis 的线程模型 Redis 以其高效的单线程模型著称&#xff0c;从设计之初&#xff0c;Redis 就选择了单线程模式&#xff0c;这在很大程度上简化了其内部实现和维护。单线程模式避免了多线程编程中常见的竞争条件和锁机制问题&#x…

Linux Bridge - Part 2

概览 在前一篇文章中&#xff0c;我描述了Linux 网桥&#xff08;bridge&#xff09;的配置&#xff0c;并展示了一个实验&#xff0c;其中使用Wireshark来分析流量。在本文中&#xff0c;我将讨论当创建一个网桥时会发生什么&#xff0c;以及Linux 网桥&#xff08;bridge&am…

C++初学者指南-4.诊断---valgrind

C初学者指南-4.诊断—Valgrind Valgrind&#xff08;内存错误检测工具&#xff09; 检测常见运行时错误 读/写释放的内存或不正确的堆栈区域使用未初始化的值不正确的内存释放&#xff0c;如双重释放滥用内存分配函数内存泄漏–非故意的内存消耗通常与程序逻辑缺陷有关&#xf…

Java版Flink使用指南——将消息写入到RabbitMQ的队列中

大纲 新建工程新增依赖 编码自动产生数据写入RabbitMQ 测试 在 《Java版Flink使用指南——从RabbitMQ中队列中接入消息流》一文中&#xff0c;我们介绍了如何使用Java在Flink中读取RabbitMQ中的数据&#xff0c;并将其写入日志中。本文将通过代码产生一些数据&#xff0c;然后…

Vine: 一种全新定义 Vue 函数式组件的解决方案

7月6日的 vue confg 大会上 ShenQingchuan 大佬介绍了他的 Vue Vine 项目&#xff0c; 一种全新定义 Vue 函数式组件的解决方案。 和 React 的函数式组件有异曲同工之妙&#xff0c;写起来直接起飞了。 让我们来快速体验一下 vine&#xff0c; 看看到底给我们带来了哪些惊喜吧…

分别通过LS和RML进行模型参数辨识matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 最小二乘法(LS)参数辨识 4.2 递归最大似然估计(RML)参数辨识 5.完整程序 1.程序功能描述 分别通过LS和RML进行模型参数辨识matlab仿真&#xff0c;仿真输出参数辨识的误差&#xff0c…

网络基础:BGP协议

BGP&#xff08;边界网关协议&#xff0c;Border Gateway Protocol&#xff09;是一种用于在不同自治系统&#xff08;Autonomous Systems&#xff0c;AS&#xff09;之间交换路由信息的路径向量协议。BGP是互联网的核心路由协议之一&#xff0c;负责管理和维护互联网范围内的路…

MySQL安全加固及等保测评

登录后复制 Mysql基础命令 create USER new_userlocalhost IDENTIFIED BY password; //创建用户 alter user root% identified with mysql_native_password by ********; //修改密码 rename user root% to root192.168.1.1; //重命名 flush privileges; …

Java面试题--JVM大厂篇之深入解析G1 GC——革新Java垃圾回收机制

目录 引言: 正文&#xff1a; 一、G1 GC的区域划分及其作用 1. 伊甸园区&#xff08;Eden Region&#xff09; 2. 幸存者区&#xff08;Survivor Region&#xff09; 3. 老年代区&#xff08;Old Generation Region&#xff09; 二、区域划分的优势: 三、图片解析: 结…

昇思25天学习打卡营第20天|LSTM+CRF序列标注

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) LSTMCRF序列标注 概述 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标…

Python | Leetcode Python题解之第220题存在重复元素III

题目&#xff1a; 题解&#xff1a; class Solution(object):def containsNearbyAlmostDuplicate(self, nums, k, t):from sortedcontainers import SortedSetst SortedSet()left, right 0, 0res 0while right < len(nums):if right - left > k:st.remove(nums[left]…

D - Go Stone Puzzle(abc361)

分析&#xff1a;因为n很小&#xff0c;可以逐一搜索&#xff0c;用一个队列将每种情况列出来&#xff0c;用bfs寻找从s到t的最短路径 #include <bits/stdc.h> using namespace std; int n; string s, t; map<string, int> dis; void bfs() { dis[s] 0; …

RocketMQ NettyRemotingServer、NettyRemotingClient 实例化、初始化、启动源码解析

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任后端开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、Java&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏&#…

【5G VoNR】VoNR流程简述

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G技术研究。 博客内容主要围绕…

go-redis源码解析:如何实现sentinel高可用

go-redis里&#xff0c;sentinel只用来获取master和从节点的ip地址&#xff0c;在获取master和replica节点ip时&#xff0c;如果sentinel不可用&#xff0c;那么会换其他的sentinel重试&#xff0c;并将可用的sentinel换到第一个 1. 用于获取master节点 先通过读锁获取c.senti…

模板进阶:非类型模板参数,类模板特化,模板的编译分离

1. 非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成常…

【Unity数据交互】如何Unity中读取Ecxel中的数据

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 专栏交流&#x1f9e7;&…

2024/7/7周报

文章目录 摘要Abstract文献阅读题目问题本文贡献问题描述图神经网络Framework实验数据集实验结果 深度学习MAGNN模型相关代码GNN为什么要用GNN&#xff1f;GNN面临挑战 总结 摘要 本周阅读了一篇用于多变量时间序列预测的多尺度自适应图神经网络的文章&#xff0c;多变量时间序…

Vulkan 学习(1)---- Vulkan 基本概念和发展历史

目录 Vulkan及其演化史Vulkan 基本概念基本术语 Vulkan 的原理Vulkan应用程序Vulkan的编程模型硬件初始化窗口展示表面资源设置流水线设置描述符和描述符缓冲池基于SPIR-V的着色器流水线管理指令的记录队列的提交 Vulkan及其演化史 目前主流的图形渲染API有OpenGL、OpenGL ES、…