C++中操作重載與類型轉換

文章目錄

  • 基本概念
    • 調用
    • 選擇作為成員還是非成員
  • 輸入和輸出運算符
  • 算術和關系運算符
    • 相等和不等運算符
    • 賦值運算符
    • 下標運算符
    • 遞增和遞減運算符
    • 成員訪問運算符
    • 函數調用運算符
      • lambda是函數對象
      • 標準庫定義的函數對象
      • 可調用對象與function
  • 重載、類型轉換與運算符
    • 類型轉換運算符
    • 避免有二義性的類型轉換

基本概念

重載的運算符是具有特殊名字的函數:它們的名字由關鍵字operator和其后要定義的運算符號共同組成。其參數數量和該運算符的運算數量一樣多。除了重載的函數調用運算符operator()之外,其他重載運算符不能含有默認參數。

如果一個運算符函數是成員函數,則它的第一個運算對象綁定到this指針。對于一個運算符函數來說,它或者是類的成員,或者至少含有一個類類型的參數:

int operator+(int, int); //err

我們可以重載大多數已有的運算符,而無權發明新的運算符號。重載后運算符和內置運算符的優先級和結合律保持一致。
在這里插入圖片描述
一般不建議重載逗號、取地址、邏輯與和邏輯或運算符。

if (a && b) { ... } 

如果 a 為 false,則 b 根本不會計算。
但一旦重載了 operator&&, operator||,這個短路規則 完全丟失,編譯器會強制先算兩個操作數,再傳給重載函數。

int x = (f1(), f2()); // 保證先執行 f1,再執行 f2  

如果重載了 operator,,它就變成了你自定義的邏輯,而失去了“順序保證”這個語言特性。

取地址則是對于類類型已經有了內置的含義。

調用

//直接調用
data1 + data2;//間接調用
operator+(data1, data2); //普通運算符
data1.operator+(data2); //成員運算符

選擇作為成員還是非成員

  • 賦值(=),下表([]),調用(()),成員訪問符(->)等必須是成員;
  • 復合運算符一般是成員;
  • 改變對象狀態的運算符或者給定類型密切相關的運算符,如遞增、遞減和解引用運算符,通常是成員;
  • 具有對稱性的運算符,例如算數、相等性、關系和位運算符等,通常是非成員;
  • 輸入輸出運算符必須是非成員(如果是成員的話,不符合第一個操作對象是輸入輸出)。

輸入和輸出運算符

//輸出
ostream &operator<<(ostream &os, const Sales_data &item);
Sales_data data;
cout << data;//輸入
istream &operator>>(istream &is, Sales_data &item)
{double price;is >> item.bookNo >> item.units_sold >> price;if (is)item.revenue = item.units_sold * price;elseitem = Sales_data();return is;
}
cin >> data;

輸入運算符通常會在還會進行錯誤處理。

算術和關系運算符

如果定義了算術運算符,一般也會定義一個對應的復合運算符:

Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{Sales_data sum = lhs;sum += rhs;return sum;
}

相等和不等運算符

相等運算符和不等運算符中的一個應該把工作委托給另外一個:

bool operator==(const Sales_data &lhs, const Sales_data &rhs)
{return lhs.isbn() == rhs.isbn() && lhs.units_sold == rhs.units_sold &&lhs.revenue == rhs.revenue;
}bool operator!=(const Sales_data &lhs, const Sales_data &rhs)
{return !(lhs == rhs);
}

賦值運算符

拷貝賦值和移動賦值可以把類的一個對象賦值給該類的另外一個對象。此外,類還可以定義其他賦值運算符以使得別的類型作為右側運算對象:

//標準庫vector類還定義了第三種賦值運算符,該運算符接受花括號元素列表作為參數
vector<string> v;
v = {"a", "an", "the"};

下標運算符

如果一個類包含下標運算符,則通常會定義兩個版本:一個返回普通的引用,另一個則是類的常量成員返回常量引用:

class StrVec
{
public:std::string& operator[](std::size_t n){return element[n];}const std::string& operator[](std::size_t n) const{	return element[n];}
private:std::string *element;
}

遞增和遞減運算符

定義遞增和遞減運算符的類應該同時定義前置和后置版本。這些運算符通常被定義為成員。

后置版本接受一個額外的int形參,該形參一般不被使用:

class StrBlobPtr
{
public://前置StrBlobPtr& operator++();StrBlobPtr& operator--();//后置StrBlobPtr operator++(int);StrBlobPtr operator--(int);
}//顯示調用
StrBlobPtr p(a1);
p.operator++(0); //后置
p.operator++(); //前置

成員訪問運算符

解引用運算符首先檢查curr是否在合理范圍內,如果是,則返回curr所指對象的一個引用;箭頭運算符調用解引用并返回其結果地址:

class StrBlobPtr
{std::string& operator*() const{auto p = check(curr, "dereference past end");return (*p)[curr];}std::string* operator->() const{return & this->operator*();}
}

值得注意的是,這兩個運算符都定義成了const成員,這是因為獲取一個元素并不會改變該對象的狀態。

StrBlob a1 = {"a", "an", "the"};
StrBlobPtr p(a1);
*p = "okay"; //給a1的首元素賦值
cout << p->size() << endl; //打印4,okay的大小
cout << (*p).size() << endl; //等價

和大多數其他運算符一樣,我們能令operator*完成任何指定的操作。箭頭運算符則不是這樣,它只能擁有成員訪問這個最基本的含義。形如point->mem的表達式來說,point必須是指向對象的指針或者是一個重載了operator->的類的對象:

(*point).mem; //point為指針
(point.operator->())->mem; //point為重載了operator->的對象

函數調用運算符

如果類重載了函數調用運算符,則我們可以像使用函數一樣使用該類的對象。一個類可以定義多個不同版本的調用運算符,相互之間在參數數量或類型上有所區別。

如果定義了函數調用運算符,則該類的對象稱作函數對象。

class PrintString
{
public:PrintString(ostream &o = cout, char c =' '): os(o), sep(c) {}void operator()(const string &s) const {os << s << sep};
private:ostream &os;char sep;
}

函數對象常常作為泛型算法的實參:

for_each(vs.begin(), vs.end(), PrintString(cerr, '\n'));

vs中的對象打印到cerr中,并換行分割。

lambda是函數對象

當我們編寫一個lambda后,編譯器會將該表達式翻譯成一個未命名類的未命名對象。該類中含有一個重載函數調用運算符:

stable_sort(words.begin(), words.end(), [](const string &a, const &b){return a.size() < b.size();});//該lambda等價于該類的一個未命名對象
class ShorterString
{
public://lambda默認值捕獲,因此生成的唯一成員函數是const//如果是引用捕獲,則不是constbool operator()(const string &a, const &b) const{return a.size() < b.size();}
};//等價lambda表達式寫法
stable_sort(words.begin(), words.end(), ShorterString());
  • 值捕獲:產生的類必須為每個值捕獲的變量建立對應的數據成員,同時構建構造函數:
auto wc = find_if(words.begin(), words.end(), [sz](const string &a){ return a.size >= sz;});class SizeComp
{
public:SizeComp(size_t n) : sz(n) {}bool operator()(const string &s) const{return s.size() >= sz;}
private:size_t sz;
};auto wc = find_if(words.begin(), words.end(), SizeComp(sz));
  • 引用捕獲:不用生成成員變量和構造函數。

標準庫定義的函數對象

標準庫定義了一組表示算術運算符、關系運算符和邏輯運算符的類,每個類分別定義了一個執行命名操作的調用運算符。
在這里插入圖片描述

sort(vec.begin(), vec.end(), greater<string>());

可調用對象與function

C++中有幾種可調用的對象:函數、函數指針、lambda表示式、bind創建的對象以及重載了函數調用運算符的類。兩種不同類型的可調用對象可以同享一種調用形式,例如:int(int, int)
在這里插入圖片描述

可以通過function表示不同類型的可調用對象:

int add(int i, int j) {return i +j;}
auto mod = [](int i ,int j) {return i % j;};
struct divide
{int operater()(int i, int j) {return i / j;}
};map<string, function<int(int, int)>> binops = 
{{"+", add},{"-", std::minus<int>()},{"/", divide()},{"*", [](int i, int j){return i * j;}},{"%", mod}
};

值得注意的是,同名可調用對象有重載時,不能存入function類型的對象中。可通過函數指針或者lambda表示式消除二義性。

重載、類型轉換與運算符

轉換構造函數和類型轉換運算符共同定義了類類型轉換。

類型轉換運算符

類型轉換運算符時類的一種特殊成員函數,它負責將一個類類型的值轉換成其它類型:operator type() const;,滿足下列條件

  • 一個類型轉換函數必須是類的成員函數;
  • 不能聲明返回類型;
  • 形參列表必須為空;
  • 函數通常是const的。
class SmallInt
{
public:SmallInt(int i = 0) : val(i) {}//雖然沒有指定返回類型,但實際會返回type類型operator int() const {return val;}
private:int val;
}

其中構造函數能夠將算術類型轉化為SmallInt,而類型轉換運算符能夠將SmallInt類型轉化為算術類型:

SmallInt si;
si = 4; //int->SmallInt
si + 3; //SmallInt->int

有時隱式的類型轉換看上去會覺得困擾,例如,當istream含有bool的類型轉換時,下面的代碼可以正常編譯:

int i = 42;
cin << i; //cin -> bool -> int

為了防止這樣的情況,可以定義顯示的類型轉換運算符:

class SmallInt
{
public:explicit SmallInt(int i = 0) : val(i) {}explicit operator int() const {return val;}
};SmallInt si = 3;
si +3; //err
static_cast<int>(si) + 3; //ok

值得注意的是,如果表達式被用作條件,則編譯器會將顯示的類型轉換自動應用于它,即顯示的類型轉換將被隱式的執行:

  • if、while、do的條件語句部分;
  • for 語句頭的條件表達式;
  • 邏輯非、或、與的運算對象;
  • 條件運算符的條件表達式。

大部分類都定義了向bool轉化的顯示類型轉換運算符,例如IO類型:

while (std::cin >> value)

避免有二義性的類型轉換

如果定義了一組類型轉換,它們的轉換源或者轉換目標可以通過其他類型轉換聯系在一起,則會產生二義性問題:

  • 最好只創建一個算術類型的轉換
//最好只創建一個算術類型的轉換
struct A
{A(int = 0);A(double);operator int() const;operator double() const; 
};void f2(long double);
A a;
f2(a); //err,二義性,不知道用哪一個進行類型轉換long lg;
A a2(lg); //err,不知道用哪一個構造函數
  • 最好不要在兩個類之間構建相同的類型轉換:
struct B;
struct A
{A() = default;A(const B&);
};struct B
{operator A() const;
};A f(const A&);
B b;
A a = f(b); //err//可以顯示指定
A a1 = f(b.operator A());
A a2 = f(A(b));

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

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

相關文章

Java學習之——“IO流“的進階流之轉換流的學習

在博主的上一篇博文中&#xff0c;詳細的介紹了“IO”流中最基本的一些知識&#xff0c;包括基本的常見的字節流和字符流&#xff0c;以及對應的緩沖流&#xff0c;對于“IO”流基礎知識相對薄弱的同學可以先去看博主的上一篇博文Java學習之——萬字詳解“IO流”中基本的字節流…

PMP考試結構、學習框架與基本術語

一、PMP考試整體結構 考試基本信息 考試形式&#xff1a;紙筆考試&#xff08;中國大陸地區&#xff09;考試時長&#xff1a;230分鐘&#xff08;約4小時&#xff09;題目數量&#xff1a;180道題 170道單選題&#xff08;四選一&#xff09;10道多選題包含5道非計分的試驗題…

淺談前端框架

在 Web 開發的演進過程中&#xff0c;前端框架扮演著越來越重要的角色。從早期的 jQuery 到如今的 React、Vue、Svelte 等&#xff0c;前端開發模式發生了翻天覆地的變化。本文將從前端框架的定義、核心特性、分類以及主流框架的差異等方面&#xff0c;帶你深入理解前端框架。 …

10.3 馬爾可夫矩陣、人口和經濟

本節內容是關于正矩陣&#xff08;postive matrices&#xff09;&#xff1a; 每個元素 aij>0a_{ij}>0aij?>0&#xff0c;它核心的結論是&#xff1a;最大的特征值為正實數&#xff0c;其對應的特征向量也是如此。 在經濟學、生態學、人口動力系統和隨機游走過程中都…

python學習進階之面向對象(二)

文章目錄 1.面向對象編程介紹 2.面向對象基本語法 3.面向對象的三大特征 4.面向對象其他語法 1.面向對象編程介紹 1.1 基本概念 概念:面向對象編程(Object-Oriented Programming, OOP)是一種流行的編程范式,它以"對象"為核心組織代碼和數據 在面向對象的世界里: …

VS+QT的編程開發工作:關于QT VS tools的使用 qt的官方幫助

加粗樣式 最近的工作用到VS2022QT5.9.9/QT5.12.9&#xff0c;在查找相關資料的時候&#xff0c;發現Qt 官方的資料還是很不錯的&#xff0c;特記錄下來&#xff0c;要記得抽時間學習下。 Add Qt versions https://doc.qt.io/qtvstools/qtvstools-how-to-add-qt-versions.html B…

【系統分析師】第21章-論文:系統分析師論文寫作要點(核心總結)

更多內容請見: 備考系統分析師-專欄介紹和目錄 文章目錄 一、寫作注意事項:構建論文的合規性與專業性 1.1 加強學習 1.2 平時積累 1.3 提高打字速度 1.4 以不變應萬變 二、試題解答方法:結構化應對策略 2.1 試題類型分析 2.2 三段式答題框架 2.3 時間分配 三、論文寫作方法:…

tailwindcss 究竟比 unocss 快多少?

tailwindcss 究竟比 unocss 快多少&#xff1f; 前言 大家好&#xff0c;我是去年一篇測評 《unocss 究竟比 tailwindcss 快多少&#xff1f;》 的作者 icebreaker。 一晃到了 2025 年&#xff0c;tailwindcss4 也正式發布了&#xff0c;現在最新版本是 4.1.13。 新版本不僅…

算法練習——55.跳躍游戲

1.題目描述給你一個非負整數數組 nums &#xff0c;你最初位于數組的 第一個下標 。數組中的每個元素代表你在該位置可以跳躍的最大長度。判斷你是否能夠到達最后一個下標&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否則&#xff0c;返回 false 。示例 1&#xff…

Django 項目6:表單與認證系統

目錄 1、form 表單 2、session 保存狀態 3、Admin 后臺 4、Auth 系統 1、form 表單 &#xff08;1&#xff09;創建 form.py 文件&#xff0c;并完善 from django import forms# 定義一個表單類 class Register(forms.Form):user forms.CharField(max_length30, label用…

tvm/triton/tensorrt比較

1.tvm的主線感覺更新太慢&#xff0c;文檔太落后&#xff0c;在自動駕駛領域不支持Blackwell平臺&#xff0c;跨平臺其實吹牛的更多。我覺得自動駕駛用不起來。2.性能最快的還是tensorrt/tensorrt_llm這條路&#xff0c;純cuda路線面臨大量cuda算子開發&#xff0c;比如vllm ll…

Transform 和BERT、GPT 模型

目錄 Transform的由來 Seq2seq 模型 Transform 的內部結構 語言模型 BERT 介紹 BERT 模型的組成 分詞器 位置編碼 Softmax 殘差結構 BERT 模型總結 Transform的由來 傳統的語?模型&#xff0c;?如RNN&#xff08;循環神經?絡&#xff09;&#xff0c;就像?個“短…

2025高教社國賽數學建模A題參考論文35頁(含代碼和模型)

2025國賽數學建模競賽A題完整參考論文 目錄 摘要 1 問題背景與重述 2 問題分析 2.1 問題一分析 2.2 問題二分析 2.3 問題三分析 2.4 問題四分析 2.5 問題五分析 3 符號說明 4 模型假設 5 模型建立與求解 5.1 問題一 煙幕有效遮蔽時長…

【Linux】常用命令匯總

【Linux】常用命令【一】tar命令【1】可用參數【2】常用案例&#xff08;1&#xff09;創建歸檔&#xff08;打包&#xff09;&#xff08;2&#xff09;查看歸檔內容&#xff1a;??&#xff08;3&#xff09;解包歸檔 (提取)&#xff1a;??【二】日志查看命令【1】基礎命令…

軟考系統架構設計師之軟件系統建模

一、軟件系統建模 系統建模流程包括如下&#xff1a; 二、人機交互設計 黃金三法則&#xff1a; 1、置于用戶控制之下以不強迫用戶進入不必要的或不希望的動作的方式來定義交互方式 提供靈活的交互 允許用戶交互可以被中斷和撤銷 當技能級別增加時可以使交互流水化并允許定制交…

Linux系統學習之注意事項及命令基本格式

哈嘍&#xff0c;你好啊&#xff0c;我是雷工&#xff01;你要是翻過歷史文章的話&#xff0c;肯定特別疑惑&#xff1a;這神經病搞得這個號到底是啥定位&#xff0c;究竟是分享啥類型的&#xff0c;咋亂七八糟的啥都有。真是個雜貨鋪啥都有&#xff0c;咋又開始分享Linux系統了…

0基礎Java學習過程記錄——枚舉、注解

一、枚舉1.基本介紹&#xff08;1&#xff09;枚舉對應英文 enumeration&#xff0c;簡寫為 enum&#xff08;2&#xff09;枚舉是一組常量的集合&#xff08;3&#xff09;可以理解為&#xff1a;枚舉屬于一種特殊的類&#xff0c;里面只包含一組有限的特定的對象2.實現方式&a…

高效計算的源泉:深入淺出馮諾依曼模型與操作系統的管理藝術 —— 構建穩定、高效的應用基石 【底層邏輯/性能優化】

???~~~~~~歡迎光臨知星小度博客空間~~~~~~??? ???零星地變得優秀~也能拼湊出星河~??? ???我們一起努力成為更好的自己~??? ???如果這一篇博客對你有幫助~別忘了點贊分享哦~??? ???如果有什么問題可以評論區留言或者私信我哦~??? ??????個人…

性能測試-jmeter9-邏輯控制器、定時器壓力并發

課程&#xff1a;B站大學 記錄軟件測試-性能測試學習歷程、掌握前端性能測試、后端性能測試、服務端性能測試的你才是一個專業的軟件測試工程師 性能測試-jmeter邏輯控制器、定時器妙用IF控制器**IF 控制器的作用**循環控制器循環控制器的作用ForEach控制器ForEach 控制器的作用…

T:線段樹入門(無區間更新)

線段樹.線段樹介紹.線段樹框架.理解線段樹.圖式整個過程.線段樹代碼逐層解析.代碼匯總.leetcode練習.線段樹介紹 線段樹(SegmentTree)\;\;\;\;\;\;\;\;線段樹(SegmentTree)線段樹(SegmentTree) is 用于高效處理區間查詢和單點修改的數據結構&#xff0c;和樹狀數組很像&#xf…