6.类和对象(4)

文章目录

  • 6.类和对象(4)
      • 回顾
      • 简单日期类的实现
        • 代码
        • 补充:前置++与后置++的重载区别
        • 补充:关于流插入运算符(<<)的解释
        • 拓展:仿照流插入操作符(<<)的作用创建一个可以直接实现日期的流插入重载
      • const成员
      • 总结

回顾

【C++语法】类和对象(3)

前面讲解了C++中类的三个关键成员函数:析构函数用于对象销毁时释放资源(如动态内存),名称格式为~类名拷贝构造函数通过同类对象初始化新对象,参数必须为引用以避免无限递归,默认浅拷贝需注意指针问题;赋值运算符重载operator=)实现对象间赋值,需返回引用以支持连续赋值,并处理自赋值和深拷贝。核心思想是:涉及资源管理(如指针)的类必须自定义这三个函数,而简单类可依赖编译器默认实现,否则可能导致内存泄漏或重复释放。

【C++语法】类和对象(2)
主要介绍了C++中的默认构造函数及其特性。文章指出空类实际上包含6个默认成员函数,其中构造函数在对象实例化时自动调用,用于初始化对象而非创建对象。构造函数具有类同名、无返回值、支持重载等特点,且不能被设为私有。编译器生成的默认构造函数对内置类型无效,但会调用自定义类型的默认构造函数。C++11允许为内置类型成员提供默认值。重点阐释了默认构造函数的概念,包括无参、全缺省和编译器生成的构造函数都属于默认构造函数,且一个类只能有一个默认构造函数。文章还强调了默认构造函数与普通构造函数的区别,并提供了实践建议和使用注意事项。

【C++语法】类和对象(1)
本文围绕C++类和对象展开。介绍对象概念,对比C与C++编程范式;讲解类定义、访问限定、作用域、实例化;说明类存储大小含内存对齐,即便无成员变量也占1字节;还阐述this指针特性,它是成员函数隐式形参。

简单日期类的实现

日期类通过规范化调整(Adjust函数)处理跨月/跨年边界,复用运算符实现高效日期计算(如+复用+=),支持完整的比较、算术和自增/自减操作,核心是闰年判断和月份天数计算。这份代码中还有一些没有提到的内容,后面会有笔记。

代码

头文件:

#pragma once
#include<iostream>
using namespace std;class Date
{friend ostream& operator<<(ostream& out, Date d);
public:// 获取某年某月的天数int GetMonthDay(int year, int month);// 全缺省的构造函数Date(int year = 1900, int month = 1, int day = 1);// 拷贝构造函数// d2(d1)Date(const Date& d);// 赋值运算符重载// d2 = d3 -> d2.operator=(&d2, d3)Date& operator=(const Date& d);// 析构函数~Date();// 日期+=天数Date& operator+=(int day);// 日期+天数Date operator+(int day) const;// 日期-天数Date operator-(int day) const;// 日期-=天数Date& operator-=(int day);// 前置++Date& operator++(int);// 后置++Date operator++();// 后置--Date operator--(int);// 前置--Date& operator--();// >运算符重载bool operator>(const Date& d) const;// ==运算符重载bool operator==(const Date& d) const;// >=运算符重载bool operator >= (const Date& d) const;// <运算符重载bool operator < (const Date& d) const;// <=运算符重载bool operator <= (const Date& d) const;// !=运算符重载bool operator != (const Date& d) const;// 日期-日期 返回天数int operator-(const Date& d) const;//调整void Adjust(Date& d);//打印void Print() const;流插入//void operator<<(ostream& out);private:int _year;int _month;int _day;
};

源文件:

#define _CRT_SECURE_NO_WARNINGS
#include"Date.h"//判断闰年
bool IsLeapYear(int year)
{if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)){return true;}return false;
}
//调整
void Date::Adjust(Date& d)
{while (d._day > GetMonthDay(d._year, d._month)){d._day -= GetMonthDay(d._year, d._month);d._month++;if (d._month > 12){d._month -= 12;d._year++;}}while (d._day <= 0){//上一个月的天数d._day += GetMonthDay(d._year, d._month - 1);d._month--;if (d._month <= 0){d._month += 12;d._year--;}}
}
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{if (month == 2){if (IsLeapYear(year)){return 29;}else{return 28;}}else{if (month == 1 || month == 3 || month == 5 ||month == 7 || month == 8 || month == 10 || month == 12){return 31;}else{return 30;}}
}
// 全缺省的构造函数
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}
// 拷贝构造函数
// d2(d1)
Date::Date(const Date& d)
{   _year = d._year;_month = d._month;_day = d._day;
}
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& Date::operator=(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;return *this;
}
// 析构函数
Date::~Date()
{//cout << "~Date()" << endl;
}
// 日期+=天数
Date& Date::operator+=(int day)
{if (day < 0){return *this -= -day;}_day += day;Adjust(*this);return *this;
}
// 日期-=天数
Date& Date::operator-=(int day)
{if (day < 0){return *this += -day;}_day -= day;Adjust(*this);return *this;
}
// 日期+天数
Date Date::operator+(int day) 
{Date tmp = *this;//方法一//tmp._day += day;//Adjust(tmp);//方法二tmp += day;return tmp;
}
// 日期-天数
Date Date::operator-(int day) 
{Date tmp = *this;//方法一//tmp._day -= day;//Adjust(tmp);//方法二tmp -= day;return tmp;
}
// 前置++
Date& Date::operator++(int)
{*this += 1;return *this;
}
// 后置++
Date Date::operator++()
{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;
}
// >运算符重载
bool Date::operator>(const Date& d) 
{if (_year > d._year){return true;}else if (_year == d._year){if (_month > d._month){return true;}else if (_month == d._month){if (_day == d._day){return true;}}}return false;
}
// ==运算符重载
bool Date::operator==(const Date& d) 
{return _year == d._year&& _month == d._month&& _day == d._day;
}
// >=运算符重载
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)
{Date max = *this;Date min = d;int flag = 1;if (min > max){min = *this;max = d;flag = -1;}int day = 0;while (min != max){++min;++day;}return day * flag;
}
//打印
void Date::Print() const
{cout << _year << "-" << _month << "-" << _day << endl;
}流插入
//void Date::operator<<(ostream& out)
//{
//	out << _year << "-" << _month << "-" << _day << endl;
//}
补充:前置++与后置++的重载区别

1>我们需要清楚前置和后置的区别:

前置是先运算在使用,后置是先使用再运算,所以运算的本质是对变量自身的值的改变,而使用是这个表达式的输出结果。因此前置运算可以直接返回运算后的值,此时用引用可以节约资源;后置需要先创建临时变量,临时变量的值不变,但形参要进行运算,最后返回这个临时变量。我们会发现前置比后置消耗的资源要少很多,自定义类型对象一般情况下最好用前置运算。

2>在C++中我们为了能把前置++/–和后置++/–进行区分,规定后置运算要在形参上加上一个int与前置构成运算符重载;
需要注意:这条规定是死的,不能定义前置运算加上int,要不然运算会出错但编译器不会报错

在这里插入图片描述

在 C++ 中,区分前置和后置自增/自减运算符重载的关键是函数签名中的占位参数 int,在 C++ 中,后置自增/自减运算符需要 int 占位参数的原因是为了解决函数重载的歧义问题。这是 C++ 语言设计者为区分前置和后置版本而制定的特殊语法规则。具体实现需要注意以下要点:

📌 区分方式

  1. 前置版本:无参数
    Date& operator++();
    Date& operator--();
  2. 后置版本:带 int 占位参数
    Date operator++(int);
    Date operator--(int);
    (调用时编译器自动传入 0 作为参数)

⚠️ 实现注意事项

特性前置版本后置版本
返回值类型返回当前对象引用 (Date&)返回操作前的临时副本 (Date)
操作逻辑先自增/自减,后返回新值先保存旧值,再自增/自减,返回旧值
效率高效(无拷贝开销)较低(需构造临时对象)
实现复用通常直接修改成员变量通常调用前置版本实现核心逻辑
链式操作支持支持(如 ++++d不支持(返回右值)
补充:关于流插入运算符(<<)的解释

现在我们可以解释为什么在C++中使用cout<<可以自己判断参数类型并进行输出了,因为在库函数源文件中就已经定义了<<多种重载形式,所以对于C++中的内置类型对象的输出可以自己识别但对于自定义类型就做不到了

在这里插入图片描述

拓展:仿照流插入操作符(<<)的作用创建一个可以直接实现日期的流插入重载

在这里插入图片描述

const成员

内置变量类型可以用const修饰,自然自定义类型也可以用const进行修饰,但是会有一些区别。

如果定义一个被const修饰的类类型的对象,在调用其成员函数的时候可能会出现权限被放大的情况

在这里插入图片描述

因为d2是被const修饰的对象,d2的值不能被修改,当我们调用d2.Printf()时,我们知道会有一个this指针变量作为隐含的参数,但这个this指针变量默
认情况下的类型是Date*const thisthis指针指向的对象(d2)的值是可以通过this指针修改的,因此编译不通过,权限被放大。

解决方法:在成员函数后面加上一个const修饰,如图,这样this指针的类型就变成了const Date*const this,不会发生权限的放大了。

在这里插入图片描述

对于前面实现的日期类或其他类,如果某成员函数的功能只是读取数据而不需要修改数据,最好在函数后面加上const修饰,这样被const修饰的对象也可以
正常使用该函数。

通过以上的学习可以回答一下这几个问题:

  1. const对象可以调用非const成员函数吗? 否

  2. 非const对象可以调用const成员函数吗? 可以

  3. const成员函数内可以调用其它的非const成员函数吗? 否

  4. 非const成员函数内可以调用其它的const成员函数吗? 可以

总结

本文实现了一个完整的C++日期类,重点包括:

核心功能:通过Adjust函数处理日期跨月/跨年边界,支持日期的加减运算(+=/-=复用实现+/-);
运算符重载:完整实现了比较运算符(>,==等)、算术运算符(±)和自增/自减(前置/后置);
关键算法:包含闰年判断和月份天数计算;
类设计:包含构造函数、拷贝控制成员、流输出友元等;
代码复用:通过+=实现+、-=实现-等技巧减少重复代码。

该日期类实现了规范的日期计算功能,通过运算符重载提供了直观的日期操作接口,是C++类和对象特性的典型应用案例。

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

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

相关文章

当凌晨的键盘声,遇见黎明的星光​

地铁玻璃映出你困倦的脸&#xff0c;耳机里的音乐循环到第 17 遍&#xff0c;早高峰的人群像沙丁鱼罐头般挤压着你。这是你每天雷打不动的三小时通勤路&#xff0c;从城市边缘到写字楼林立的 CBD&#xff0c;窗外的风景换了四季&#xff0c;而你始终困在摇晃的车厢里&#xff0…

Web Worker技术详解与应用场景

我们来详细探讨一下 Web Worker。它是现代 Web 开发中解决 JavaScript 单线程限制、提升应用性能和响应能力的关键技术。 核心问题&#xff1a;JavaScript 的单线程模型 浏览器 UI 线程&#xff08;主线程&#xff09;&#xff1a;JavaScript 在浏览器中默认运行在单个线程&a…

React Next快速搭建前后端全栈项目并部署至Vercel

很好&#xff0c;你是想搞清楚Next.js 的后端结构和传统 Node Express 的区别对比&#xff0c;我来整理一套结构化、精准、对面试有说服力的解答&#xff0c;并附示意结构图。 01Next vs Express 、## ⚡️1️⃣ Next.js 后端是怎么构建的 Next.js 在默认情况下本身就集成后…

【T宝客户项目解决过程】01-模型训练

1 项目需求描述 博主自己开了一家T宝店&#xff0c;有一个客户有这个需求&#xff1a;有一大堆图像&#xff0c;大概有10多万张图&#xff0c;都是比较小尺寸的图。各种类型都有&#xff0c;我们想要通过将不同类型发图像进行分开&#xff0c;如何实现呢&#xff1f; 2 思路 …

如何在中将网络改为桥接模式并配置固定IP地址

在使用服务器搭建虚拟机的过程中&#xff0c;我们发现有许多场景需要将虚拟机的网络配置为桥接模式&#xff0c;并为其设置固定的IP地址。为了帮助大家更高效地进行网络配置&#xff0c;提升虚拟机的连接稳定性和管理便捷性&#xff0c;我们总结了这篇指南&#xff0c;介绍如何…

强化学习 - 基于策略的Reinforce算法

&#x1f3af; REINFORCE 策略梯度算法推导&#xff08;完整&#xff09; 1. 目标函数定义 我们希望最大化策略的期望回报&#xff1a; J ( θ ) E τ ∼ π θ [ R ( τ ) ] J(\theta) \mathbb{E}_{\tau \sim \pi_\theta} \left[ R(\tau) \right] J(θ)Eτ∼πθ​​[R(τ…

Windows Sever Core安装及常用命令

一、Windows Sever Core 在安装 Windows Server 的过程中&#xff0c;可以选择“Server Core”&#xff08;核心安装&#xff09;这种没有图形用户界面&#xff08;GUI&#xff09;的安装方式。这种模式下&#xff0c;Windows Server 主要通过命令行或远程管理进行配置和维护&…

Java 单元测试实战:以“两数之和”为例,讲透测试思维

&#x1f31f;Java 单元测试实战&#xff1a;以“两数之和”为例&#xff0c;讲透测试思维 在 Java 开发中&#xff0c;单元测试不仅是验证功能正确的手段&#xff0c;更是衡量开发者是否具备“测试思维”的标志。今天我们通过一个最简单的功能——“两数之和”来系统讲解如何…

Bootstrap 5学习教程,从入门到精通,Bootstrap 5 提示框(Tooltips) 语法知识点及案例(21)

Bootstrap 5 提示框(Tooltips) 语法知识点及案例 一、提示框语法知识点 1. 基本概念 提示框(Tooltips)是当用户悬停在元素上时显示的小浮动标签&#xff0c;用于提供额外信息。 2. 核心属性 data-bs-toggle"tooltip" - 标识元素为提示框触发器title - 提示框显示…

设计模式实战指南:从源码解析到Java后端架构的艺术

&#x1f3af; 设计模式实战指南&#xff1a;从源码解析到Java后端架构的艺术 概述 本文档基于设计模式分类&#xff0c;详细介绍Java后端开发中各种设计模式的实际应用场景&#xff0c;结合Spring、MyBatis、Redis等主流框架的源码分析&#xff0c;帮助开发者深入理解设计模…

Python Arrow 库详解:更智能的日期时间处理

1. 安装与基本用法 安装 Arrow pip install arrow基本使用示例 import arrow# 获取当前时间 now arrow.now() print(now) # 输出: 2023-07-15T14:30:45.12345608:00# 创建特定时间 dt arrow.get(2023-07-15 14:30:00, YYYY-MM-DD HH:mm:ss) print(dt) # 输出: 2023-07-15T…

大家电破渠道困局,小家电攻用户体验,云徙有何解法?

中国家电行业正经历深刻转型。 自2018年市场规模触及8400亿顶峰后&#xff0c;行业从增量竞争转向存量博弈。与此同时&#xff0c;线上渠道在2023年首次以58%的占比超越线下&#xff0c;其中扫地机器人等小家电品类线上渗透率突破90%。消费需求也在同步重构——从家庭场景向个…

DMDPC多副本数据分布测试

需求&#xff1a;测试建表和插入数据是否会根据分布列进行自动分发。 验证方法&#xff1a;1&#xff09;准备基础环境&#xff1a;创建用户和表空间。2&#xff09;创建数据分布测试表&#xff0c;并插入数据。3&#xff09;查询指定分区数据&#xff0c;验证数据分布情况。 …

Qt/C++开发监控GB28181系统/rtp解包/jrtplib库的使用/同时支持udp和tcp被动和主动三种方式解包

一、前言说明 通过sip协议仅仅是交互&#xff0c;音视频数据的收发最终并不是通过sip传输的&#xff0c;而是通过将数据打包成rtp的格式再通过udp或者tcp通信的&#xff0c;sip协议仅仅是告知对方待会要往哪里发数据&#xff0c;是udp还是tcp。由于数据都是rtp包格式&#xff…

集群聊天服务器---muduo库的使用

使用 C 和 muduo 网络库来实现一个简单的聊天服务器和客户端。 服务器端&#xff1a; class chatServer { public:// 初始化TcpServerchatServer(muduo::net::EventLoop *loop,const muduo::net::InetAddress &listenAddr): _server(loop, listenAddr, "chatServer&…

关于Net Core Web API 项目测试 数据库模拟的两种不同方法 DC_week 6

1.关系型数据库 插件&#xff1a;Microsoft.EntityFrameworkCore.InMemory Microsoft.EntityFrameworkCore.InMemory 是一个用于测试的“临时内存数据库”&#xff0c;让你在不连接真实数据库的情况下&#xff0c;测试 EF Core 的功能。 使用时就是用具体这个框架里面已经…

如何获取 vscode 的 vsix 离线插件安装包

1、搜索所需要的插件 Extensions for Visual Studio family of products | Visual Studio Marketplace网址 2、点击 Repository 跳转到对应的 git 仓库 3、在 git 仓库依次选择 main , Tags, View all tags 4、选择你想下载的版本&#xff0c;并点击 downloads 5、往下滑动&…

ULS23 挑战:用于计算机断层扫描中 3D 通用病变分割的基准模型及基准数据集|文献速递-深度学习医疗AI最新文献

Title 题目 The ULS23 challenge: A baseline model and benchmark dataset for 3D universal lesion segmentation in computed tomography ULS23 挑战&#xff1a;用于计算机断层扫描中 3D 通用病变分割的基准模型及基准数据集 01 文献速递介绍 每年进行的CT检查数量持续…

WebSocket 端点 vs Spring Bean

在websocket端点中注入业务service时&#xff0c;不能像普通的springbean一样通过Autowired或Resource注解进行注入。主要原因是websocket端点与spring容器中的bean的生命周期管理容器不同。 WebSocket 端点&#xff08;ServerEndpoint&#xff09;和 Spring Bean 的生命周期存…

MySQL8:jdbc插入数据后获取自增ID

pom文件&#xff1a; <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"&…