【C++面向對象】封裝(下):探索C++運算符重載設計精髓

🔥個人主頁 🔥

😈所屬專欄😈?


每文一詩? 💪🏼

? ? ?年年歲歲花相似,歲歲年年人不同 —— 唐/劉希夷《代悲白頭翁》

? ? ? ? 譯文:年年歲歲繁花依舊,歲歲年年看花之人卻不相同


目錄

C++運算符重載概念

C++運算符重載語法及作用

加減乘除運算符重載

賦值運算符重載

返回值作為引用以實現鏈式賦值

關系運算符重載

大于號運算符重載

相等運算符重載

全部代碼


C++運算符重載概念

????????在 C++ 里,運算符重載屬于多態的一種表現形式,它允許你為自定義的數據類型重新定義運算符的行為。

運算符重載其實就是對已有的運算符賦予新的功能,使它能夠處理自定義類型的對象

C++運算符重載語法及作用

語法:返回類型 operator運算符(參數列表) {}

  1. 提高代碼可讀性:在處理自定義類型時,使用重載后的運算符能讓代碼更直觀、自然。例如,對于自定義的復數類,你可以重載+運算符,讓兩個復數相加的操作就像普通數字相加一樣簡單。
  2. 實現自定義類型的操作:借助運算符重載,你能為自定義類型實現像內置類型那樣的操作。比如,對于自定義的矩陣類,你可以重載+-*等運算符,實現矩陣的加減乘運算。
  3. 代碼復用與一致性:運算符重載可以復用已有的運算符,使代碼更具一致性。對于熟悉內置運算符的開發者來說,重載后的運算符也易于理解和使用。

加減乘除運算符重載

  1. 作類成員函數重載
#include <iostream>class human
{
public:human(){};human(int a ,int b):m_a(a),m_b(b){}//加減乘除運算符重載(成員函數)human operator*(const human& h){human h2;h2.m_a = this->m_a * h.m_a;h2.m_b = this->m_b * h.m_b;return h2;}int geta(){return this->m_a;}int getb(){return this->m_b;}private:int m_a;int m_b;};int main(int argc, char const *argv[])
{human h(10,20);human h1(2,10);//相當于 h.operator*(h1)human h3 = h * h1;std::cout << h3.geta() <<" "<< h3.getb()<<std::endl;return 0;
}

代碼解讀:

  • 提供默認無參構造函數函數初始化列表
     human(){};human(int a ,int b):m_a(a),m_b(b){}
  • 加減乘除運算符重載(成員函數)這里指的是乘法
    human operator*(const human& h){human h2;h2.m_a = this->m_a * h.m_a;h2.m_b = this->m_b * h.m_b;return h2;}
  1. 這里的參數是指常量引用目的是不使用值傳遞的方式,防止對數據進行大量的拷貝,常量引用本質使用地址傳遞,地址通常占有4個字節,程序效率更高
  2. 這里的this指向調用該重載函數的哪個對象。
  3. 棧區重新創建一個對象h,并將運算后的成員變量更新到該對象并返回。
  • human h3 = h * h1;

    這個相當于 h.operator*(h1)

  • std::cout << h3.geta() <<" "<< h3.getb()<<std::endl;

    因為成員變量m_a和m_b時私有變量,不能在類外訪問,所以在類內使用函數來訪問。

  • 輸出

這樣就實現了類型為human的變量使用運算符來進行運算,20 = 10 *2 ;200 = 20*10

其他加減除法以此類推。

2.作全局函數重載

#include <iostream>class human
{friend human operator*(const human& p2, int val);
public:human(){};human(int a ,int b):m_a(a),m_b(b){}//加減乘除運算符重載(成員函數)human operator*(const human& h){human h2;h2.m_a = this->m_a * h.m_a;h2.m_b = this->m_b * h.m_b;return h2;}int geta(){return this->m_a;}int getb(){return this->m_b;}private:int m_a;int m_b;};
// 加減乘除運算符重載(全局函數)訪問私有成員需要加friend聲明
human operator*(const human& p2, int val)
{human temp;temp.m_a = p2.m_a *val;temp.m_b = p2.m_b * val;return temp;
}int main(int argc, char const *argv[])
{human h(10,20);human h1(2,10);//相當與 operator*(h,90)human h2 = h * 90;//相當于 h.operator*(h1)human h3 = h * h1;std::cout << h2.geta() <<" "<< h2.getb()<<std::endl;std::cout << h3.geta() <<" "<< h3.getb()<<std::endl;return 0;
}

代碼解讀:

  • ?加減乘除運算符重載(全局函數)訪問私有成員需要加friend聲明
human operator*(const human& p2, int val)
{human temp;temp.m_a = p2.m_a *val;temp.m_b = p2.m_b * val;return temp;
}

????????該函數在類外定義,在類外定義的函數不能訪問類內的私有變量,但是可以通過友元函數的方法來讓類外定義的全局函數訪問類內的私有變量。即

    friend human operator*(const human& p2, int val);
  • human h2 = h * 90;

    ?相當與 operator*(h,90)調用的是全局函數。

  • 輸出

賦值運算符重載

返回值作為引用以實現鏈式賦值

#include <iostream>class human
{public:human(){};human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}//賦值運算符重載human& operator=(const human& h){if(this != &h){if(m_c != nullptr){delete m_c;m_c = nullptr;}}m_c = new int(*h.m_c);return *this;}int geta(){return this->m_a;}int getb(){return this->m_b;}int getc(){return *this->m_c;}
private:int m_a;int m_b;int* m_c;};int main(int argc, char const *argv[])
{human h(10,20,12);human h1(2,10,34);human h2(22,10,4);h = h1 = h2;std::cout <<h.getc()<<std::endl;return 0;
}

?代碼解讀:

  • 函數初始化列表來初始化成員變量

        human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}
  • 賦值運算符重載

     human& operator=(const human& h){if(this != &h){if(m_c != nullptr){delete m_c;m_c = nullptr;}}m_c = new int(*h.m_c);return *this;}

    ????? 首先使用this來判斷調用該函數的對象是否和傳入的對象是同一塊內存,接著在判斷調用該成員函數的對象中的成員變量m_c是否不為空,不為空則釋放內存,再置為空。

兩個對象不是同一塊內存(this != &h)需要delete,再重新開辟內存。

  • 這樣作的目的是用于:如果對一個成員變量是指針的對象,將該指針指向的值進行修改時,需要先釋放調用該重載函數的對象中指針變量,再為空,然后在m_c = new int(*h.m_c);重新開辟一塊內存,內存的值是參數中對象的那個值。

  • 原因:若不釋放這塊內存,在賦值操作完成后,原來的內存就會變成無法訪問的 “孤兒” 內存,程序無法再釋放它,從而造成內存泄漏。執行 delete m_c; 就能正確釋放這塊內存,讓系統可以重新使用它。

兩個對象是同一塊內存(this == &h)不需要delete。

  • 原因:當兩個對象是統一塊內存時,m_c這個指針變量指向的值是一樣的,如果對其執行 delete m_c,那么在后面m_c = new int(*h.m_c);時使用*h.m_c時是非法的,因為這塊內存已經被釋放掉了,不能訪問。

為什么要m_c = new int(*h.m_c);

  • 原因:編譯器默認提供的是淺拷貝,對于一個含有指針變量的類中,淺拷貝會對這塊內存多次釋放,是不合法的,所以需要執行深拷貝來使得兩個對象中的指針變量指向的值相同,但指針的值不同(地址不同)
  • 有關【C++面向對象】封裝(上):探尋構造函數的幽微之境-CSDN博客深拷貝,淺拷貝【C++面向對象】封裝(上):探尋構造函數的幽微之境-CSDN博客

為什么return *this;返回值類型是human&引用。

  • 原因:this指向調用該成員函數的那個對象,而*this是指的該對象,而返回值是human&引用是為了實現鏈式調用,而返回值是human不能實現鏈式賦值,因為他返回的是該對象的拷貝。

關系運算符重載

大于號運算符重載

#include <iostream>class human
{friend bool operator>(const human& h1,int val);
public:human(){};human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}//關系運算符重載bool operator>(const human& h1){if(this->m_a > h1.m_a && this->m_b > h1.m_b)return true;elsereturn false;}int geta(){return this->m_a;}int getb(){return this->m_b;}int getc(){return *this->m_c;}
private:int m_a;int m_b;int* m_c;};//關系運算符重載(全局函數)訪問私有成員需要加friend聲明
bool operator>(const human& h1,int val){if(val< h1.m_a && val < h1.m_b)return true;elsereturn false;
}
int main(int argc, char const *argv[])
{human h(10,20,12);human h1(2,10,34);//相當于 h.operator>(h1)if(h > h1)std::cout<<"h>h1"<<std::endl;elsestd::cout<<"h<h1"<<std::endl;//相當與 operator>(h,12)if(h > 12)std::cout<<"h>12"<<std::endl;elsestd::cout<<"h<12"<<std::endl;return 0;
}

?代碼解讀:

上段代碼分別用成員函數和全局函數的方法來實現對自定義類型的判斷

  • 對于
h > h1

相當于 h.operator>(h1)

  • 對于
h > 12

相當與 operator>(h,12)

  • 輸出

相等運算符重載

#include <iostream>class human
{friend bool operator==(const human& h1,int val);
public:human(){};human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}//關系運算符重載bool operator==(const human& h1){if(this->m_a == h1.m_a && this->m_b == h1.m_b)return true;elsereturn false;}int geta(){return this->m_a;}int getb(){return this->m_b;}int getc(){return *this->m_c;}
private:int m_a;int m_b;int* m_c;};//關系運算符重載(全局函數)訪問私有成員需要加friend聲明
bool operator==(const human& h1,int val){if(val == h1.m_a)return true;elsereturn false;
}int main(int argc, char const *argv[])
{human h(10,20,12);human h1(2,10,34);//相當于 h.operator==(h1)if(h == h1)std::cout<<"二者相等"<<std::endl;elsestd::cout<<"二者不等"<<std::endl;//相當與 operator==(h,23)if(h == 23)std::cout<<"二者相等"<<std::endl;elsestd::cout<<"二者不等"<<std::endl;return 0;
}

??代碼解讀:

上段代碼分別用成員函數和全局函數的方法來實現對自定義類型的相等判斷

  • 對于
h == h1

相當于相當于 h.operator==(h1)

  • 對于
h == 23

相當于 operator==(h,23)

  • 輸出

全部代碼

#include <iostream>class human
{friend human operator*(const human& p2, int val);friend bool operator==(const human& h1,int val);friend bool operator>(const human& h1,int val);
public:human(){};human(int a ,int b,int c):m_a(a),m_b(b),m_c(new int(c)){}//加減乘除運算符重載(成員函數)human operator*(const human& h){human h2;h2.m_a = this->m_a * h.m_a;h2.m_b = this->m_b * h.m_b;return h2;}//關系運算符重載bool operator==(const human& h1){if(this->m_a == h1.m_a && this->m_b == h1.m_b)return true;elsereturn false;}//關系運算符重載bool operator>(const human& h1){if(this->m_a > h1.m_a && this->m_b > h1.m_b)return true;elsereturn false;}//賦值運算符重載human& operator=(const human& h){if(this != &h){if(m_c != nullptr){delete m_c;m_c = nullptr;}}m_c = new int(*h.m_c);return *this;}int geta(){return this->m_a;}int getb(){return this->m_b;}int getc(){return *this->m_c;}
private:int m_a;int m_b;int* m_c;};
// 加減乘除運算符重載(全局函數)訪問私有成員需要加friend聲明
human operator*(const human& p2, int val)
{human temp;temp.m_a = p2.m_a *val;temp.m_b = p2.m_b * val;return temp;
}//關系運算符重載(全局函數)訪問私有成員需要加friend聲明
bool operator==(const human& h1,int val){if(val == h1.m_a)return true;elsereturn false;
}
//關系運算符重載(全局函數)訪問私有成員需要加friend聲明
bool operator>(const human& h1,int val){if(val< h1.m_a && val < h1.m_b)return true;elsereturn false;
}
int main(int argc, char const *argv[])
{human h(10,20,12);human h1(2,10,34);//相當與 operator*(h,90)human h2 = h * 90;//相當于 h.operator*(h1)human h3 = h * h1;//相當于 h.operator==(h1)if(h == h1)std::cout<<"二者相等"<<std::endl;elsestd::cout<<"二者不等"<<std::endl;//相當與 operator==(h,23)if(h == 23)std::cout<<"二者相等"<<std::endl;elsestd::cout<<"二者不等"<<std::endl;//相當于 h.operator>(h1)if(h > h1)std::cout<<"h>h1"<<std::endl;elsestd::cout<<"h<h1"<<std::endl;//相當與 operator>(h,12)if(h > 12)std::cout<<"h>12"<<std::endl;elsestd::cout<<"h<12"<<std::endl;std::cout << h2.geta() <<" "<< h2.getb()<<std::endl;std::cout << h3.geta() <<" "<< h3.getb()<<std::endl;h = h1;std::cout <<h.getc()<<std::endl;return 0;
}

?🔥個人主頁 🔥

😈所屬專欄😈?

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

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

相關文章

從代碼學習深度學習 - Transformer PyTorch 版

文章目錄 前言1. 位置編碼(Positional Encoding)2. 多頭注意力機制(Multi-Head Attention)3. 前饋網絡與殘差連接(Position-Wise FFN & AddNorm)3.1 基于位置的前饋網絡(PositionWiseFFN)3.2 殘差連接和層規范化(AddNorm)4. 編碼器(Encoder)4.1 編碼器塊(Enco…

閱讀分析Linux0.11 /boot/head.s

目錄 初始化IDT、IDTR和GDT、GDTR檢查協處理器并設置CR0寄存器初始化頁表和CR3寄存器&#xff0c;開啟分頁 初始化IDT、IDTR和GDT、GDTR startup_32:movl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gslss _stack_start,%espcall setup_idtcall setup_gdtmovl $0x1…

33、單元測試實戰練習題

以下是三個練習題的具體實現方案&#xff0c;包含完整代碼示例和詳細說明&#xff1a; 練習題1&#xff1a;TDD實現博客評論功能 步驟1&#xff1a;編寫失敗測試 # tests/test_blog.py import unittest from blog import BlogPost, Comment, InvalidCommentErrorclass TestBl…

16-算法打卡-哈希表-兩個數組的交集-leetcode(349)-第十六天

1 題目地址 349. 兩個數組的交集 - 力扣&#xff08;LeetCode&#xff09;349. 兩個數組的交集 - 給定兩個數組 nums1 和 nums2 &#xff0c;返回 它們的 交集 。輸出結果中的每個元素一定是 唯一 的。我們可以 不考慮輸出結果的順序 。 示例 1&#xff1a;輸入&#xff1a;nu…

SciPy庫詳解

SciPy 是一個用于數學、科學和工程計算的 Python 庫&#xff0c;它建立在 NumPy 之上&#xff0c;提供了許多高效的算法和工具&#xff0c;用于解決各種科學計算問題。 CONTENT 1. 數值積分功能代碼 2. 優化問題求解功能代碼3. 線性代數運算功能代碼 4. 信號處理功能代碼 5. 插…

杰弗里·辛頓:深度學習教父

名人說&#xff1a;路漫漫其修遠兮&#xff0c;吾將上下而求索。—— 屈原《離騷》 創作者&#xff1a;Code_流蘇(CSDN)&#xff08;一個喜歡古詩詞和編程的Coder&#x1f60a;&#xff09; 杰弗里辛頓&#xff1a;當堅持遇見突破&#xff0c;AI迎來新紀元 一、人物簡介 杰弗…

BladeX單點登錄與若依框架集成實現

1. 概述 本文檔詳細介紹了將BladeX認證系統與若依(RuoYi)框架集成的完整實現過程。集成采用OAuth2.0授權碼流程&#xff0c;使用戶能夠通過BladeX賬號直接登錄若依系統&#xff0c;實現無縫單點登錄體驗。 2. 系統架構 2.1 總體架構 #mermaid-svg-YxdmBwBtzGqZHMme {font-fa…

初識Redis · set和zset

目錄 前言&#xff1a; set 基本命令 交集并集差集 內部編碼和應用場景 zset 基本命令 交集并集差集 內部編碼和應用場景 應用場景&#xff08;AI生成&#xff09; 排行榜系統 應用背景 設計思路 熱榜系統 應用背景 設計思路 熱度計算方式 總結對比表 前言&a…

playwright 教程高級篇:掌握網頁自動化與驗證碼處理等關鍵技術詳解

Playwright 教程高級篇:掌握網頁自動化與驗證碼處理等關鍵技術詳解 本教程將帶您一步步學習如何使用 Playwright——一個強大的瀏覽器自動化工具,來完成網頁任務,例如提交鏈接并處理旋轉驗證碼。我們將按照典型的自動化流程順序,從啟動瀏覽器到關閉瀏覽器,詳細講解每個步驟…

數據結構(完)

樹 二叉樹 構建二叉樹 int value;Node left;Node right;public Node(int val) {valueval;} 節點的添加 Node rootnull;public void insert(int num) {Node nodenew Node(num);if(rootnull) {rootnode;return;}Node index root;while(true) {//插入的節點值小if(index.value&g…

FastAPI與SQLAlchemy數據庫集成與CRUD操作

title: FastAPI與SQLAlchemy數據庫集成與CRUD操作 date: 2025/04/16 09:50:57 updated: 2025/04/16 09:50:57 author: cmdragon excerpt: FastAPI與SQLAlchemy集成基礎包括環境準備、數據庫連接配置和模型定義。CRUD操作通過數據訪問層封裝和路由層實現,確保線程安全和事務…

一個基于Django的寫字樓管理系統實現方案

一個基于Django的寫字樓管理系統實現方案 用戶現在需要我用Django來編寫一個寫字樓管理系統的Web版本&#xff0c;要求包括增刪改查寫字樓的HTML頁面&#xff0c;視頻管理功能&#xff0c;本地化部署&#xff0c;以及人員權限管理&#xff0c;包含完整的代碼結構和功能實現&am…

mongodb在window10中創建副本集的方法,以及node.js連接副本集的方法

創建Mongodb的副本集最好是新建一個文件夾&#xff0c;如D:/data&#xff0c;不要在mongodb安裝文件夾里面創建副本集&#xff0c;雖然這樣也可以&#xff0c;但是容易造成誤操作或路徑混亂&#xff1b;在新建文件夾里與現有 MongoDB 數據隔離&#xff0c;避免誤操作影響原有數…

Maven 多倉庫與鏡像配置全攻略:從原理到企業級實踐

Maven 多倉庫與鏡像配置全攻略&#xff1a;從原理到企業級實踐 一、核心概念&#xff1a;Repository 與 Mirror 的本質差異 在 Maven 依賴管理體系中&#xff0c;repository與mirror是構建可靠依賴解析鏈的兩大核心組件&#xff0c;其核心區別如下&#xff1a; 1. Repositor…

STM32 四足機器人常見問題匯總

文章不介紹具體參數&#xff0c;有需求可去網上搜索。 特別聲明&#xff1a;不論年齡&#xff0c;不看學歷。既然你對這個領域的東西感興趣&#xff0c;就應該不斷培養自己提出問題、思考問題、探索答案的能力。 提出問題&#xff1a;提出問題時&#xff0c;應說明是哪款產品&a…

MySQL 中 `${}` 和 `#{}` 占位符詳解及面試高頻考點

文章目錄 一、概述二、#{} 和 ${} 的核心區別1. 底層機制代碼示例 2. 核心區別總結 三、為什么表名只能用 ${}&#xff1f;1. 預編譯機制的限制2. 動態表名的實現 四、安全性注意事項1. ${} 的風險場景2. 安全實踐 五、面試高頻考點1. 基礎原理類問題**問題 1**&#xff1a;**問…

C語言編譯預處理2

#include <XXXX.h>和#include <XXXX.c> #include "XXXX.h" 是 C 語言中一條預處理指令 #include <XXXX.h>&#xff1a;這種形式用于包含系統標準庫的頭文件。預處理器會在系統默認的頭文件搜索路徑中查找XXXX.h 文件。例如在 Linux 系統中&#…

Elasticvue-輕量級Elasticsearch可視化管理工具

Elasticvue一個免費且開源的 Elasticsearch 在線可視化客戶端&#xff0c;用于管理 Elasticsearch 集群中的數據&#xff0c;完全支持 Elasticsearch 版本 8.x 和 7.x. 功能特色&#xff1a; 集群概覽索引和別名管理分片管理搜索和編輯文檔REST 查詢快照和存儲庫管理支持國際…

Git提交規范及最佳實踐

Git 提交規范通常是為了提高代碼提交的可讀性、可維護性和自動化效率&#xff08;如生成 ChangeLog&#xff09;。以下是常見的 Conventional Commits 規范&#xff0c;結合社區最佳實踐總結而成&#xff1a; 1. 提交格式 每次提交的 commit message 應包含三部分&#xff1a;…

Ubuntu中snap

通過Snap可以安裝眾多的軟件包。需要注意的是&#xff0c;snap是一種全新的軟件包管理方式&#xff0c;它類似一個容器擁有一個應用程序所有的文件和庫&#xff0c;各個應用程序之間完全獨立。所以使用snap包的好處就是它解決了應用程序之間的依賴問題&#xff0c;使應用程序之…