【C++語法】類和對象(4)——日期類和const成員函數

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"&…