C++ 運算符重載(一) | 輸入/輸出,相等/不等,復合賦值,下標,自增/自減,成員訪問運算符

文章目錄

  • 輸出運算符<<
  • 輸入運算符>>
  • 相等/不等運算符
  • 復合賦值運算符
  • 下標運算符
  • 自增/自減運算符
  • 成員訪問運算符


輸出運算符<<

通常情況下,輸出運算符的第一個形參是一個 非常量ostream對象的引用 。之所以 ostream 是非常量是因為向流寫入內容會改變其狀態;而該形參是引用是因為我們無法直接復制一個 ostream 對象。

第二個形參一般來說是一個 常量的引用,該常量是我們想要打印的類類型。第二個形參是引用的原因是我們希望避免復制實參;而之所以該形參可以是常量是因為(通常情況下)打印對象不會改變對象的內容。

為了與其他輸出運算符保持一致,operator<< 一般要返回它的 ostream 形參。

通常我們需要在類中重載 << 以避免查看成員時輸出操作過于繁瑣:

class A {friend ostream& operator<<(ostream& os, const A& a);int i = 1;double d = 3.14;
};
ostream& operator<<(ostream& os, const A& a) {os << a.i << " " << a.d;return os;
}

在這里插入圖片描述

值得注意的幾點:

  1. 減少格式化操作(如:換行符): 目的是給用戶更大的自由去決定輸出的格式,如果我們自帶換行,那么用戶就無法在同一行內解接著打印一些描述性文本了。
  2. 輸入輸出運算符必須是非成員函數: 如果是某個類的成員函數,則輸入輸出運算符也必須是 istreamostream 成員(詳見下文),但是這兩個類(istreamostream)屬于標準庫,而我們無法給標準庫中的類添加任何成員。
  3. 可以將IO運算符聲明為友元: 既然我們的 IO操作 又想訪問類的私有成員,又不能是類的成員函數,那么聲明成友元是最佳選擇。

用例子來解釋一下第二點:

我們都知道重載運算符的返回類型一定要與它的實際操作相匹配,因此,重載 == 返回值為 bool ;重載 + 返回值為 類的引用 ……

輸入輸出運算符是 IO類 的成員函數,因此其返回類型是 IO類本身 ,那么如果某個類將 重載的輸入輸出運算符 作為 成員函數 的話,返回類型 就會變成 這個類本身,重載的輸入輸出運算符的左側運算對象則是這個類的一個對象:

class B {int i = 1;double d = 3.14;
public:ostream& operator<<(ostream& os) {os << i << " " << d;return os;}
};
B b;
b << cout; // 這樣調用不符合我們的輸出習慣

如此一來改變了 << 的調用方式,也就不算構成重載了,如果既要 cout << b;,還要 <<B 的成員。那么就要在 ostream類 中添加 ostream& operator<<(B&); ,可正如前文所說,ostream 屬于標準庫,我們無法給標準庫中的類添加任何成員。ostream<< 的各類重載如下:
在這里插入圖片描述


輸入運算符>>

  • 通常情況下,輸入運算符的第一個形參是運算符將要讀取的流的引用
  • 第二個形參是將要讀入到的對象的引用(對象不能是常量,因為將數據讀入到這個對象中實際是修改了這個對象)
  • 通常會返回某個給定流的引用

與輸出運算符不同的是,輸入運算符必須處理輸入失敗的情況:

class A {friend istream& operator>>(istream& is, A& a);friend ostream& operator<<(ostream& os, const A& a);int i = 1;double d = 3.14;
};istream& operator>>(istream& is, A& a) {int i1;double d1;is >> i1 >> d1;if (is) {a.i = i1;a.d = d1;}else a = A();return is;
}ostream& operator<<(ostream& os, const A& a) {os << a.i << " " << a.d;return os;
}

重寫輸入運算符后,可以對輸入的數據進行對應處理:
在這里插入圖片描述
當沒有輸入/輸入錯誤時,用構造函數創建一個臨時量,然后調用賦值運算符為 a 賦值:
在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述

當有多個輸入時,對輸入進行處理:
在這里插入圖片描述
在這里插入圖片描述


相等/不等運算符

對于類而言,判斷相等需要比較每一項數據成員,因此有必要對相等運算符進行重載。

如果定義了 operator==,則這個類也應該定義 operator!=。對于用戶來說,當他們能使用 == 時肯定也希望能使用 !=,反之亦然。

相等運算符和不相等運算符中的一個應該把工作委托給另外一個,這意味著其中一個運算符應該負責實際比較對象的工作,而另一個運算符則只是調用那個真正工作的運算符。

class A {friend istream& operator>>(istream& is, A& a);friend ostream& operator<<(ostream& os, const A& a);int i = 1;double d = 3.14;
public:bool operator==(const A& a) {return this->d == a.d && this->i == a.i;}bool operator!=(const A& a) {return !(*this == a);}
};

在這里插入圖片描述


復合賦值運算符

復合賦值運算符不一定非得是類的成員,不過我們還是傾向于把包括復合賦值在內的所有賦值運算都定義在類的內部。為了與內置類型的復合賦值保持一致,類中的復合賦值運算符也要返回其左側運算對象的引用

PS:賦值運算符必須是類的成員

A& operator+=(const A& a) {i += a.i;d += a.d;return *this;
}

在這里插入圖片描述


下標運算符

  • 下標運算符必須是成員函數。
  • 為了與下標的原始定義兼容,下標運算符通常以所訪問元素的引用作為返回值,這樣做的好處是下標可以出現在賦值運算符的任意一端。
  • 如果一個類包含下標運算符,則它通常會定義兩個版本:一個返回普通引用,另一個是類的常量成員并且返回常量引用。
class IntVec // IntVec是對標準庫vector類的模仿,僅存儲int元素
{int* begin; // 指向已分配的內存中的首元素int* end; // 指向最后一個實際元素之后的位置int* cap; // 指向分配的內存末尾之后的位置
public:int& operator[](int n) { return begin[n]; }const int& operator[](int n) const { return begin[n]; } // 第二個const修飾*this
};

下標運算符返回的是元素的引用,當 IntVec 是非常量時,我們可以給元素賦值;而我們對常量對象取下標時,不能對其賦值。


自增/自減運算符

與內置類型一樣,重載的自增自減同時要有前置版本后置版本

要想同時定義前置和后置運算符,必須首先解決一個問題,即普通的重載形式無法區分這兩種情況。

為了解決這個問題,后置版本接受一個額外的(不被使用)int 類型的形參。當我們使用后置運算符時,編譯器為這個形參提供一個值為 0 的實參。盡管從語法上來說后置函數可以使用這個額外的形參,但是在實際過程中通常不會這么做。這個形參的唯一作用就是區分前置版本和后置版本的函數,而不是真的要在實現后置版本時參與運算。

前置版本:

// 僅作偽代碼實現
類名& operator++();
類名& operator--();

后置版本:

為了與內置版本保持一致,后置運算符應該返回對象的原值(遞增或遞減之前的值),返回的形式是一個值而非引用。

類名 operator++(int); // 我們不會用到int形參,因此無需為它命名。
類名 operator--(int);
// 舉個例子,但不詳細實現Ptr類了,可以將它理解為 IntVec(或真正的順序容器) 的指針類
Ptr operator++(int){Ptr ret = *this; // 記錄當前值++*this; // 調用前置++運算符,前置++需要檢查自增的有效性return res; // 返回之前記錄的狀態
}
/* 顯式地調用后置運算符 */
Ptr p(v); // p指向v中的vector
p.operator++(0); // 調用后置版本,盡管0會被忽略,卻必不可少,因為編譯器只有通過它才知道應該使用后置版本。
p.operator++(); // 調用前置版本

成員訪問運算符

箭頭運算符(->)必須是類的成員。 解引用運算符(*)則無硬性要求。

// 偽代碼
class Ptr{
public:int& operator*() const {// 檢查解引用對象是否在規定范圍內return *p[下標]; // *p可以是形如vector的對象}int* operator->() const {return & this->operator*(); //將工作委托給解引用運算符}
};

較之解引用運算符,重載箭頭運算符有些限制,重載的箭頭運算符必須返回類的指針或者自定義了箭頭運算符的某個類的對象。

例如,對于形如 p->mem 的表達式來說,根據 p 類型的不同,表達式分別等價于:

(*p).mem; // p是一個內置的指針類型
p.operator()->mem; // p是類的對象

除此之外,代碼都將發生錯誤。p->mem 的執行過程如下所示:
1.如果 p 是指針,則我們應用內置的箭頭運算符,首先解引用該指針,然后從所得的對象中獲取指定的成員。如果 p 所指的類型沒有名為 mem 的成員,程序會發生錯誤。
2.如果 p 是定義了 operator-> 的類的一個對象,如果 p.operator()-> 的結果是一個指針,則執行第1步;如果該結果本身含有重載的 operator->(),則重復調用當前步驟。最終,當這一過程結束時程序或者返回了所需的內容,或者返回一些表示程序錯誤的信息。

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

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

相關文章

C++ 重載函數調用運算符 | 再探lambda,函數對象,可調用對象

文章目錄重載函數調用運算符lambdalambda等價于函數對象lambda等價于類標準庫函數對象可調用對象與function可調用對象function函數重載與function重載函數調用運算符 函數調用運算符必須是成員函數。 一個類可以定義多個不同版本的調用運算符&#xff0c;互相之間應該在參數數…

C++ 運算符重載(二) | 類型轉換運算符,二義性問題

文章目錄類型轉換運算符概念避免過度使用類型轉換函數解決上述問題的方法轉換為 bool顯式的類型轉換運算符類型轉換二義性重載函數與類型轉換結合導致的二義性重載運算符與類型轉換結合導致的二義性類型轉換運算符 概念 類型轉換運算符&#xff08;conversion operator&#…

Tomcat中JVM內存溢出及合理配置

Tomcat本身不能直接在計算機上運行&#xff0c;需要依賴于硬件基礎之上的操作系統和一個Java虛擬機。Tomcat的內存溢出本質就是JVM內存溢出&#xff0c;所以在本文開始時&#xff0c;應該先對Java JVM有關內存方面的知識進行詳細介紹。 一、Java JVM內存介紹 JVM管理兩種類型的…

俄羅斯農民乘法 | 快速乘

文章目錄概念概念 俄羅斯農民乘法經常被用于兩數相乘取模的場景&#xff0c;如果兩數相乘已經超過數據范圍&#xff0c;但取模后不會超過&#xff0c;我們就可以利用這個方法來拆位取模計算貢獻&#xff0c;保證每次運算都在數據范圍內。 A 和 B 兩數相乘的時候我們如何利用加…

Linux網絡編程 | socket選項設定 及 網絡信息API

文章目錄讀取和設置 socket 選項SO_REUSEADDRSO_RCVBUF 和 SO_SNDBUFSO_RCVLOWAT 和 SO_SNDLOWATSO_LINGER 選項網絡信息APIgethostbyname 和 gethostbyaddrgetservbyname 和 getservbyportgetaddrinfogetnameinfo讀取和設置 socket 選項 正如 fcntl 系統調用是控制文件描述符…

Linux | 高級I/O函數

文章目錄創建文件描述符的函數pipe函數dup函數、dup2函數讀取或寫入數據readv函數、writev函數零拷貝sendfile函數splice函數tee函數進程間通信——共享內存mmap函數 和 munmap函數控制文件描述符fcntl函數創建文件描述符的函數 pipe函數 不再贅述&#xff0c;詳情見我的另一…

分布式理論:CAP、BASE | 分布式存儲與一致性哈希

文章目錄分布式理論CAP定理BASE理論分布式存儲與一致性哈希簡單哈希一致性哈希虛擬節點分布式理論 CAP定理 一致性&#xff08;Consistency&#xff09;&#xff1a; 在分布式系統中的所有數據副本&#xff0c;在同一時刻是否一致&#xff08;所有節點訪問同一份最新的數據副…

Tomcat服務器性能優化

一、概述 本文檔主要介紹了Tomcat的性能調優的原理和方法。可作為公司技術人員為客戶Tomcat系統調優的技術指南&#xff0c;也可以提供給客戶的技術人員作為他們性能調優的指導手冊。 二、調優分類 由于Tomcat的運行依賴于JVM&#xff0c;從虛擬機的角度我們把Tomcat的調整分為…

分布式系統概念 | 分布式事務:2PC、3PC、本地消息表

文章目錄分布式事務2PC&#xff08;二階段提交協議&#xff09;執行流程優缺點3PC&#xff08;三階段提交協議&#xff09;執行流程優缺點本地消息表&#xff08;異步確保&#xff09;分布式事務 分布式事務就是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分…

數據結構算法 | 單調棧

文章目錄算法概述題目下一個更大的元素 I思路代碼下一個更大元素 II思路代碼132 模式思路代碼接雨水思路算法概述 當題目出現 「找到最近一個比其大的元素」 的字眼時&#xff0c;自然會想到 「單調棧」 。——三葉姐 單調棧以嚴格遞增or遞減的規則將無序的數列進行選擇性排序…

最長下降子序列

文章目錄題目解法DP暴搜思路代碼實現貪心二分思路代碼實現題目 給出一組數據 nums&#xff0c;求出其最長下降子序列&#xff08;子序列允許不連續&#xff09;的長度。&#xff08;類似于lc的最長遞增子序列&#xff09; 示例&#xff1a; 輸入&#xff1a; 6 // 數組元素個…

Linux 服務器程序規范、服務器日志、用戶、進程間的關系

文章目錄服務器程序規范日志rsyslogd 守護進程syslog函數openlog函數setlogmask函數closelog函數用戶進程間的關系進程組會話系統資源限制改變工作目錄和根目錄服務器程序后臺化服務器程序規范 Linux 服務器程序一般以后臺進程&#xff08;守護進程[daemon]&#xff09;形式運…

IO模型 :阻塞IO、非阻塞IO、信號驅動IO、異步IO、多路復用IO

文章目錄IO模型阻塞IO非阻塞IO信號驅動IO多路復用IO異步IOIO模型 根據各自的特性不同&#xff0c;IO模型被分為阻塞IO、非阻塞IO、信號驅動IO、異步IO、多路復用IO五類。 最主要的兩個區別就是阻塞與非阻塞&#xff0c;同步與異步。 阻塞與非阻塞 阻塞與非阻塞最主要的區別就…

Tomcat服務器集群與負載均衡實現

一、前言 在單一的服務器上執行WEB應用程序有一些重大的問題&#xff0c;當網站成功建成并開始接受大量請求時&#xff0c;單一服務器終究無法滿足需要處理的負荷量&#xff0c;所以就有點顯得有點力不從心了。另外一個常見的問題是會產生單點故障&#xff0c;如果該服務器壞掉…

Linux服務器 | 事件處理模式:Reactor模式、Proactor模式

文章目錄Reactor模式Proactor模式同步I/O模型模擬Proactor模式兩者的優缺點ReactorProactor同步I/O模型通常用于實現 Reactor 模式&#xff0c;異步I/O模型通常用于實現 Proactor 模式。&#xff08;不是絕對的&#xff0c;同步I/O也可模擬出 Proactor 模式&#xff09; React…

Linux服務器 | 服務器模型與三個模塊、兩種并發模式:半同步/半異步、領導者/追隨者

文章目錄兩種服務器模型及三個模塊C/S模型P2P模型I/O處理單元、邏輯單元、存儲單元并發同步與異步半同步/半異步模式變體&#xff1a;半同步/半反應堆模式改進&#xff1a;高效的半同步/半異步模式領導者/追隨者模式組件 &#xff1a;句柄集、線程集、事件處理器工作流程兩種服…

香農信息熵之可憐的小豬

文章目錄題目解析香農熵公式樣例具體分析代碼題目 有 n 桶液體&#xff0c;其其中 正好 有一桶含有毒藥&#xff0c;其裝的都是水。它們從外觀看起來都一樣。為了弄清楚哪只水桶含有毒藥&#xff0c;你可以喂一些豬喝&#xff0c;通過觀察豬是否會死進行判斷&#xff0c;實驗對…

字符串匹配之KMP(KnuthMorrisPratt)算法(圖解)

文章目錄最長相等前后綴next數組概念代碼實現圖解GetNext中的回溯改進代碼實現代碼復雜度分析最長相等前后綴 給出一個字符串 ababa 前綴集合&#xff1a;{a, ab, aba, abab} 后綴集合&#xff1a;{a, ba, aba, baba} 相等前后綴 即上面用同樣顏色標識出來的集合元素&#…

linux下tomcat6.0與jdk安裝詳細步驟

安裝Tomcat6.0和JDK1.6 在linux系統上安裝tomcat和jdk應該說是我學習linux知識的第一課了&#xff0c;之前只 是聽說過&#xff0c;從沒接觸過&#xff0c;但我們公司項目都是部署在linux系統上的&#xff0c;那天上司突 然給我發了幾個文檔&#xff0c;讓我看一下&#xff…

Android入門(一) | Android Studio的配置與使用

文章目錄安裝配置Android Studio使用Android Studio模擬器更改Android SDK的路徑Hello World&#xff01;安裝配置Android Studio 從這一步開始&#xff1a; 一直點 next 即可&#xff0c;直到存儲路徑的選擇上&#xff0c;可以放到非 C 盤&#xff0c;這里我放到 D 盤了&am…