C++ 運算符重載與操作符重載

目錄

運算符重載

運算符重載的特性

其他運算符重載的實現

默認成員函數——賦值運算符重載

默認成員函數——取地址操作符重載

const成員

附錄


運算符重載

C++為了增強代碼的可讀性引入了運算符重載,運算符重載是具有特殊函數名的函數,也具有其返回值類型函數名字以及參數列表

運算符重載實際上就如同函數重載,使操作符擁有新的功能。

結構:

返回值類型 operator操作符(參數列表)

例如

bool operator==(Date d1,Date d2);

定義一個Date類

class Date
{
public://構造函數Date(int year = 0, int month = 0, int day = 0){//判斷日期是否合法//GetMonthDay()獲取這個月的天數if (month > 0 && month < 13 &&(day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;}}
private:int _year;//年int _month;//月int _day;//日
};

我們都知道==是用來比較的運算符,Date類對象進行比較該怎么比較呢?我們可以規定,如果兩個對象的年、月、日都相當則兩個對象相等,返回true

錯誤示例

class Date
{
public://構造函數Date(int year = 0, int month = 0, int day = 0){if (month > 0 && month < 13&& (day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}}bool operator==(Date d1, Date d2){return (d1._year == d2._year) && (d1._month == d2._month) && (d1._day == d2._day);}
private:int _year;int _month;int _day;};

二元運算符的重載函數的參數有兩個,規定第一個參數左操作數第二個參數右操作數

還記得成員函數有什么特性嗎?成員函數有一個自帶的參數this,類型為類類型。因為我們不可能將this指針刪掉,所以只能省略第一個參數

為減少拷貝引起的消耗,盡量使用引用的方式傳參

正確的做法

class Date
{
public://...bool operator==(const Date& d)//若不改變形參最好用const修飾{return (_year == d._year) && (_month == d._month) && (_day == d._day);}//...	
};

運算符重載的特性

運算符重載有如下特性:

  • 重載操作符必須有一個類類型參數;
  • 不能通過連接其他符號來創建新的操作符:比如operator@、operator?等;
  • 用于內置類型的運算符,其含義不能改變,例如:int類型的+,不能改變其含義;
  • 作為類成員函數重載時,其形參看起來比操作數數目少1,因為成員函數的第一個參數為隱藏的this;
  • .* :: sizeof ?: .注意以上5個運算符不能重載。這個經常在筆試選擇題中出現。

其他運算符重載的實現

有了上述的==作為示例,我們還可以實現< > <= >= + - ++ --等一系列操作符的重載。

<? ? >? ? ?<=? ? >=? ? ? != 重載

class Date
{
public://構造函數//...bool operator==(const Date& d){return (_year == d._year) && (_month == d._month) && (_day == d._day);}bool operator<(const Date& d) {return _year < d._year|| (_year == d._year && _month < d._month)|| (_year == d._year && _month == d._month && _day < d._day);}bool operator<=(const Date& d) {//函數的復用return *this < d || *this == d;}bool operator>(const Date& d) {//函數的復用return !(*this <= d);}bool operator>=(const Date& d) {//函數的復用return !(*this < d);}bool operator!=(const Date& d) {//函數的復用return !(*this == d);}
//...
};

+=? ?-=? ? ?+? ? ?-

注意:下列四個運算符的右操作數都為天數

class Date
{
public://...//獲取當月的天數int GetMonthDay(int year, int month) {assert(month > 0 && month < 13);int monthArray[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;}else{return monthArray[month];}}//+= 返回自身的引用,減少拷貝Date& operator+=(int day){//判斷是否加了負數if (day < 0){//復用*this -= -day;return *this;}_day += day;while (_day > GetMonthDay(_year, _month));{_day -= GetMonthDay(_year, _month);//進位_month++;if (_month == 13){_year++;_month = 1;}}return *this;}//-= 返回自身的引用,減少拷貝Date& operator-=(int day){//判斷是否減了一個負數if (day < 0){//復用*this += -day;return *this;}_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;}Date operator+(int day) {//拷貝構造//因為加不改變自身的值,所以創建臨時對象Date tmp(*this);//復用tmp += day;return tmp;}Date operator-(int day){Date tmp(*this);tmp -= day;return tmp;}
//...
};

前置++ 與 后置++ 重載

前置++和后置++都是一元運算符,為了讓前置++與后置++能形成正確重載,C++規定:

  • 后置++重載時多增加一個int類型的參數,但調用函數時該參數不用傳遞,編譯器
    自動傳遞
class Date
{
public://...//前置++Date& operator++(){*this += 1;return *this;}//后置++// 注意:后置++是先使用后+1,因此需要返回+1之前的舊值,// 故需在實現時需要先將this保存一份,然后給this + 1// 而temp是臨時對象,因此只能以值的方式返回,不能返回引用Date operator++(int){Date tmp(*this);*this += 1;return tmp;}//前置--Date& operator--(){*this -= 1;return *this;}//后置--Date operator--(int){Date tmp(*this);*this -= 1;return tmp;}
//...
};

日期 - 日期的實現

日期+日期沒有意義,但是日期-日期有意義,日期-日期代表相距多少天

class Date
{
//...int operator-(const Date& d){Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;}//...
}

<<? ? >>? 重載

錯誤示例

class Date
{
//...//使用因為返回,為了適應連續輸入或輸出的情況ostream& operator<<(ostream& out){out << _year << "年" << _month << "月" << _day << "日" << endl;return out;}istream& operator>>(istream& in){in >>_year >>_month >>_day;return in;}//...
}

>> <<?是二元操作符,上文中提到二元操作符第一個參數為左操作數,第二個參數為右操作數。此時這段代碼第一個參數為this,也就意味著左操作數變成了對象,右操作數變成了cout。那么我們使用時只能這樣寫:

void Test2()
{Date d1(2023, 4, 1);d1 << cout;
}

雖然能滿足需求,但是用起來感覺怪怪的。由于我們無法改變this的位置,所以只能使用其它辦法來實現<< >>的重載了。

這里我們只能將重載定義在類的外面才能避開this的影響。但是類外的函數又訪問不了類的私有成員。我們只能通過將重載函數設置為類友元函數來實現了。

class Date
{
//...//申明友元函數friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);//...
}
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日";return out;
}istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}

默認成員函數——賦值運算符重載

與之前講的構造函數與析構函數等默認成員函數相同,賦值運算符重載也屬于6個默認成員函數之一。

1. 作為與眾不同的默認成員函數,其有以下特性:

  • 賦值運算符重載格式:
  • 參數類型:const T&,傳遞引用可以提高傳參效率;
  • 返回值類型:T&,返回引用可以提高返回的效率,有返回值目的是為了支持連續賦值檢測是否自己給自己賦值;
  • 返回*this :要復合連續賦值的含義;

賦值重載

class Date
{
//...Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}//...
}

2. 賦值運算符只能重載成類的成員函數不能重載成全局函數

錯誤示例

class Date
{//...
};
// 賦值運算符重載成全局函數,注意重載成全局函數時沒有this指針了,需要給兩個參數
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}

此種情況會出現編譯錯誤error C2801: “operator =”必須是非靜態成員。

  • 出錯原因是:賦值運算符如果不顯式實現,編譯器會生成一個默認的。此時用戶再在類外自己實現一個全局的賦值運算符重載,就和編譯器在類中生成的默認賦值運算符重載沖突了,故賦值運算符重載只能是類的成員函數。

3. 用戶沒有顯式實現時,編譯器會生成一個默認賦值運算符重載,以值的方式逐字節拷貝。注意:內置類型成員變量是直接賦值的,而自定義類型成員變量需要調用對應類的賦值運算符重載完成賦值。

這里賦值重載與拷貝構造函數的特性非常相似。
?

默認成員函數——取地址操作符重載

6個默認成員函數只剩兩個——取地址重載與const取地址重載。但是,這兩個函數實在沒有實現的必要,因為我們自己實現與編譯器自動實現出來的效果是一樣的。

class Date
{//...Date* operator&(){return this;}const Date* operator&()const{return this;}//...
};

const成員

將const修飾的成員函數稱之為const成員函數,const修飾類成員函數,實際修飾該成員函數隱含的this指針,表明在該成員函數中不能對類的任何成員進行修改。

什么情況下需要用const修飾?

我們可能暫時感受不到const修飾的作用,但是遇到如下情況,const修飾就非常有必要了。

class Date
{
public://...void print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year;int _month;int _day;
}void Test3()
{Date d1(2023, 4, 1);d1.print();const Date d2(2022, 3, 1);d2.print();
}

報錯內容為:“void Date::print(void)”: 不能將“this”指針從“const Date”轉換為“Date &”。

這里是典型的權限放大錯誤,我們不能將const Date* &d2傳遞給形參Date* this。

改正的辦法為同樣用const修飾this,但具體的寫法可不像我們想的那樣。

void print() const
{cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

因為我們無法顯式的修改this,所以C++規定在函數的后面加上const即為修飾this

附錄

我們總結上文中的運算符重載,整理一下完整的日期類的實現。此處我們使用多文件的形式實現—>

  • Date.h文件中進行頭文件包含命名空間展開類的聲明內聯函數定義等;
  • Date.cpp文件中進行對類成員函數的定義。
#define _CRT_SECURE_NO_DEPRECATE 1
#include<iostream>
#include<assert.h>
using namespace std;// 類里面短小函數,適合做內聯的函數,直接是在類里面定義的
class Date
{// 友元函數聲明friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);public:Date(int year = 0, int month = 0, int day = 0);void Print() const;int GetMonthDay(int year, int month) 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;bool operator>=(const Date& d) const;Date& operator+=(int day);Date operator+(int day) const;Date& operator-=(int day);Date operator-(int day) const;int operator-(const Date& d) const;Date& operator=(const Date& d);//前置++Date& operator++();// 后置++// int參數 僅僅是為了占位,跟前置重載區分Date operator++(int);// 前置--Date& operator--();// 后置--Date operator--(int);//取地址重載Date* operator&();const Date* operator&() const;private:int _year;int _month;int _day;
};inline ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日";return out;
}inline istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
#define _CRT_SECURE_NO_DEPRECATE 1
#include"Date.h"
//構造函數
Date::Date(int year, int month , int day)
{//判斷日期是否合法if (month > 0 && month < 13 &&(day > 0 && day <= GetMonthDay(year, month))){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;}
}bool Date::operator==(const Date& d) const
{return (_year == d._year) && (_month == d._month) && (_day == d._day);
}bool Date::operator<(const Date& d) const
{return _year < d._year|| (_year == d._year && _month < d._month)|| (_year == d._year && _month == d._month && _day < d._day);
}bool Date::operator<=(const Date& d) const
{//函數的復用return *this < d || *this == d;
}bool Date::operator>(const Date& d) const
{//函數的復用return !(*this <= d);
}bool Date::operator>=(const Date& d) const
{//函數的復用return !(*this < d);
}bool Date::operator!=(const Date& d) const
{//函數的復用return !(*this == d);
}//獲取當月的天數
int Date::GetMonthDay(int year, int month) const
{assert(month > 0 && month < 13);int monthArray[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;}else{return monthArray[month];}
}//+= 返回自身的引用
Date& Date::operator+=(int day)
{//判斷是否加了負數if (day < 0){//復用*this -= -day;return *this;}_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){//復用*this += -day;return *this;}_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}Date Date::operator+(int day) const
{//拷貝構造//因為加不改變自身的值,所以創建臨時對象Date tmp(*this);//復用tmp += day;return tmp;
}Date Date::operator-(int day) const
{Date tmp(*this);tmp -= day;return tmp;
}//前置++
Date& Date::operator++()
{*this += 1;return *this;
}//后置++
// 注意:后置++是先使用后+1,因此需要返回+1之前的舊值,
// 故需在實現時需要先將this保存一份,然后給this + 1
// 而temp是臨時對象,因此只能以值的方式返回,不能返回引用
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;
}int Date::operator-(const Date& d) const
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}Date& Date::operator=(const Date& d)
{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}Date* Date::operator&() 
{return this;
}
const Date* Date::operator&() const
{return this;
}

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/209768.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/209768.shtml
英文地址,請注明出處:http://en.pswp.cn/news/209768.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用Vue實現一個當鼠標懸浮時出現,鼠標離開時消失的雙層菜單

前言 分享一個基于Vue實現一個當鼠標懸浮時出現&#xff0c;鼠標離開時消失的雙層菜單。同時也是為了以后需要類似的需求時&#xff0c;可以提供一個實現思路&#xff0c;以及可以快速ctrlc和ctrlv操作&#xff0c;提高工作效率~ 一、示例代碼 &#xff08;1&#xff09;/sr…

Linux C/C++ 分析網絡流量(十六進制TCP數據包分析)

在分析TCP數據包時&#xff0c;理解TCP協議的工作原理和報文格式是關鍵。TCP是一種面向連接的、提供可靠的、端到端的字節流傳輸服務。其頭部結構包括源端口、目標端口、序列號、確認應答號等字段。序列號是在建立連接時由計算機生成的隨機數作為初始值&#xff0c;每發送一次數…

使用Pytorch從零開始實現BERT

生成式建模知識回顧: [1] 生成式建模概述 [2] Transformer I&#xff0c;Transformer II [3] 變分自編碼器 [4] 生成對抗網絡&#xff0c;高級生成對抗網絡 I&#xff0c;高級生成對抗網絡 II [5] 自回歸模型 [6] 歸一化流模型 [7] 基于能量的模型 [8] 擴散模型 I, 擴散模型 II…

前端食堂技術周刊第 107 期:技術播客節、Deno Cron、FEDAY、XState v5、Electron 2023 生態系統回顧

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;烤椰拿鐵 食堂技術周刊倉庫地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童歐巴。歡迎來到前端食堂技術周刊&#xff0c;我們先來看下…

圖論與網絡優化3

CSDN 有字數限制&#xff0c;因此筆記分別發布&#xff0c;目前&#xff1a; 【筆記1】概念與計算、樹及其算法【筆記2】容量網絡模型、遍歷性及其算法【筆記3】獨立集及其算法 6 獨立集及其算法 6.1 獨立集和覆蓋 6.1.1 獨立數和覆蓋數 獨立集&#xff1a;設 S ? V ( G …

PaddleDetection系列2--NCCL安裝及測試

NCCL安裝及測試 1 系統信息查看1.1 查看本機的操作系統和位數信息&#xff1a;1.2 確認處理器架構1.3 確認cuda版本 2 NCCL安裝2.1 根據上面的系統架構以及CUDA版本&#xff0c;進入[官網](https://developer.nvidia.com/nccl/nccl-download)下載匹配的nccl&#xff0c;若想獲取…

力扣44題通配符匹配題解

44. 通配符匹配 - 力扣&#xff08;LeetCode&#xff09; 給你一個輸入字符串 (s) 和一個字符模式 (p) &#xff0c;請你實現一個支持 ? 和 * 匹配規則的通配符匹配&#xff1a; ? 可以匹配任何單個字符。* 可以匹配任意字符序列&#xff08;包括空字符序列&#xff09;。 …

【ITK庫學習】使用itk庫進行圖像濾波ImageFilter:梯度Gradient

目錄 1、itkGradientImageFilter2、itkGradientMagnitudeImageFilter 梯度強度3、itkGradientMagnitudeRecursiveGaussianImageFilter 帶濾波的梯度強度4、itkDerivativeImageFilter 不帶濾波的導函數 1、itkGradientImageFilter 該類是一個基類&#xff0c;用于使用方向導數計…

C++筆試題之回文數的判斷

“回文”是指正讀反讀都能讀通的句子&#xff0c;它是古今中外都有的一種修辭方式和文字游戲&#xff0c;如“我為人人&#xff0c;人人為我”等。在數學中也有這樣一類數字有這樣的特征&#xff0c;成為回文數&#xff08;palindrome number&#xff09;。 設n是一任意自然數…

MSSQL 程序集使用方法

1.C# 寫一個程序 1.1新建一個項目【類庫【.Net FrameWork】 1.2編寫代碼 刪除 namespace ApiSQLClass { } 代碼如下&#xff1a;【具體調用API模式根據具體編寫】 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.…

1. 使用poll或epoll創建echo服務器

1. 說明&#xff1a; 此篇博客主要記錄一種客戶端實現方式&#xff0c;和兩種使用poll或者epoll分別創建echo服務器的方式&#xff0c;具體可看代碼注釋&#xff1a; 2. 相關代碼&#xff1a; 2.1 echoClient.cpp #include <iostream> #include <cstdio> #incl…

C語言中的 sizeof 運算符

在 C 語言中&#xff0c;sizeof 是一個運算符&#xff0c;用于獲取給定類型或變量的字節大小。它返回一個 size_t 類型的值&#xff0c;表示以字節為單位的對象大小。 sizeof 運算符有以下特點&#xff1a; 用法&#xff1a;sizeof 運算符可以應用于數據類型或表達式。計算靜…

酷開科技以創新為動力用大數據提升品牌認知

在21世紀的今天&#xff0c;我們生活在一個被互聯網深深改變的世界。互聯網不僅改變了我們的生活方式&#xff0c;也正在改變我們的思維方式和工作方式。而互聯網作為一種新的發展趨勢&#xff0c;更是為我們提供了無數的機會和無限可能性&#xff0c;從電子商務時代到社交網絡…

CSP-何以包郵?

題目描述 新學期伊始&#xff0c;適逢頓頓書城有購書滿 x 元包郵的活動&#xff0c;小 P 同學欣然前往準備買些參考書。 一番瀏覽后&#xff0c;小 P 初步篩選出 n 本書加入購物車中&#xff0c;其中第 i 本&#xff08;1≤i≤n&#xff09;的價格為 ai 元。 考慮到預算有限&am…

scala編碼

1、Scala高級語言 Scala簡介 Scala是一門類Java的多范式語言&#xff0c;它整合了面向對象編程和函數式編程的最佳特性。具體來講Scala運行于Java虛擬機&#xff08;JVM)之上&#xff0c;井且兼容現有的Java程序&#xff0c;同樣具有跨平臺、可移植性好、方便的垃圾回收等特性…

ubuntu server 20.04 備份和恢復 系統 LTS

ubuntu server 20.04 備份和恢復 系統 LTS tar命令系統備份與恢復&#xff08;還原or新裝&#xff09; 備份系統 cd / su root tar cvpzf backup.tgz --exclude/tmp --exclude/run --exclude/dev --exclude/snap --exclude/proc --exclude/lostfound --exclude/backup.tgz …

啟動游戲出現concrt140.dll錯誤的8種解決方法

在計算機使用過程中&#xff0c;我們經常會遇到一些錯誤提示&#xff0c;其中之一就是找不到concrt140.dll文件。這個錯誤通常會導致程序無法正常運行&#xff0c;給用戶帶來困擾。本文將介紹找不到concrt140.dll無法繼續執行代碼的8個方法&#xff0c;同時探討concrt140.dll丟…

LinuxBasicsForHackers筆記 -- 文件系統和存儲設備管理

設備目錄/dev Linux 有一個特殊的目錄&#xff0c;其中包含代表每個連接設備的文件&#xff1a;相應命名的 /dev 目錄。 /dev中有很多設備列表。 特別令人感興趣的是設備 sda1、sda2、sda3、sdb 和 sdb1&#xff0c;它們通常是硬盤驅動器及其分區以及 USB 閃存驅動器及其分區…

理解基于 Hadoop 生態的大數據技術架構

轉眼間&#xff0c;一年又悄然而逝&#xff0c;時光荏苒&#xff0c;歲月如梭。當回首這段光陰&#xff0c;不禁感嘆時間的匆匆&#xff0c;仿佛只是一個眨眼的瞬間&#xff0c;一年的旅程已成為過去&#xff0c;而如今又到了畫餅的時刻了 &#xff01; 基于 Hadoop 生態的大數…