【C++初階】—— 類和對象 (下)

📝個人主頁🌹:EterNity_TiMe_
?收錄專欄?:C++ “ 登神長階 ”
🌹🌹期待您的關注 🌹🌹

在這里插入圖片描述

在這里插入圖片描述

類和對象

  • 1. 運算符重載
    • 運算符重載
    • 賦值運算符重載
    • 前置++和后置++重載
  • 2. 成員函數的補充
  • 3. 初始化列表
    • 初始化列表的概念
    • 初始化列表的特征
    • explicit關鍵字
  • 4. static成員
  • 5. 友元
  • 6. 類的匿名對象
  • 7. 總結


前言:類的6個默認成員函數,我們了解三個,講完剩下的成員函數,其實類和對象的大致內容已經結束,最后我們在了解一些C++類和對象的剩下的的細節,我們就正式結束類和對象

如果你還對前面三個默認成員函數不太了解,建議先閱讀這篇博客
類的成員函數


1. 運算符重載

運算符重載

在一個自定義變量里,如果我們想實現對它的加減乘除,是無法直接使用的,因此C++為了增強代碼的可讀性引入了運算符重載,運算符重載是具有特殊函數名的函數

關鍵字operator 后面接需要重載的運算符符號

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

舉個例子:

// 重載 ==
bool operator==(const Date& d)
{return _year = d._year;&& _month = d._month;&& _day = d._day;
}

注意:

  • 重載操作符必須有一個自定義類型參數
  • 運算符重載定義在類外時不能訪問類中的私有成員,因此重載成成員函數
  • 作為類成員函數重載時,成員函數的第一個參數為隱藏的this

賦值運算符重載

1. 關于賦值運算符重載:

  • 參數類型:const T&,傳遞引用可以提高傳參效率
  • 返回值類型:T&,返回引用可以提高返回的效率,有返回值目的是為了支持連續賦值
  • 檢測是否自己給自己賦值
  • 返回 *this

我們以下例子將使用日期類
例如:

class Date
{
public:Date(){}Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << ' ' << _month << ' ' << _day << endl;}Date& operator=(const Date& d){// 檢查是否給直接賦值if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 5, 23);Date d2 = d1;// Date d2;// 實際上operator=的調用// d2.operator=(d1);d1.Print();d2.Print();return 0;
}

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

// 假設我們在類外面重載成全局函數
// 注意:在類外是沒有 this 指針的
Date& operator=(Date& this, const Date& d)
{if (&this != &d){this._year = d._year;this._month = d._month;this._day = d._day;}return this;
}

我們將寫好的代碼拿去運行一下,我們發現無法編譯
在這里插入圖片描述

其實,賦值運算符比較特殊如果不顯式實現,編譯器會生成一個默認的。如果在類外自己實現一個全局的賦值運算符重載,就和編譯器在類中生成的默認賦值運算符重載沖突了

在這里插入圖片描述


3. 用戶沒有顯式實現時,編譯器會生成一個默認賦值運算符重載,以值的方式逐字節拷貝

這里我們要格外注意:
系統默認生成一個默認賦值運算符重載它和之前的拷貝構造一樣,發生的是淺拷貝,內置類型成員變量可以直接使用,而自定義類型成員變量需要我們自己調用對應類的賦值運算符重載


前置++和后置++重載

關于前置++和后置++:

  • 前置++:返回+1之后的結果
  • 后置++:是先使用后+1,因此需要返回+1之前的舊值

格式:

  • 因為前置++和后置++符號一樣,我們為了要想正確完成重載,C++規定,后置++重載時多增加一個int類型的參數,但調用函數時該參數不用傳遞,編譯器自動傳遞
// 前置++
Date& operator++()
{_day += 1;return *this
}
// 后置++
Date& operator++(int)
{Date temp(*this);_day += 1;return temp;
}
// 前置--
Date& operator--()
{_day -= 1;return *this
}
// 后置--
Date& operator--(int)
{Date temp(*this);_day -= 1;return temp;
}

最后補充一點,關于運算符重載,并不是所有的運算符都需要重載,而是要根據自定義的類需要重載哪些運算符!

注意以下運算符不能重載:

  • .*
  • ::
  • sizeof
  • ?:
  • .

講到這里類和對象的大致內容已經結束,剩下兩個成員函數,我們簡單了解一下


2. 成員函數的補充

const成員

將const修飾的“成員函數”稱之為const成員函數,const修飾類成員函數,實際修飾該成員函數隱含的this指針,表明在該成員函數中不能對類的任何成員進行修改
在這里插入圖片描述

例如:

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}void Print() const{cout << "Print()const" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{// 編譯器會優先調用符合的函數,如果沒有則會根據權限來調用// 本質是:權限能縮小,但是不能放大// 及非const對象可以調用const成員函數// 非const成員函數內可以調用其它的const成員函數Date d1(2024,5,23);d1.Print();const Date d2(2024,5,23);d2.Print();
}

取地址及const取地址操作符重載

這兩個默認成員函數一般不用重新定義 ,編譯器默認會生成!

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

這兩個運算符一般不需要重載,使用編譯器生成的默認取地址的重載即可,只有特殊情況,才需要重載,我們能夠修改別人獲取的地址


3. 初始化列表

  • 在創建對象時,編譯器通過調用構造函數,給對象中各個成員變量一個合適的初始值
  • 對象中有了一個初始值,因此構造函數體中的語句只能將其稱為賦初值,而不能稱作初始化。因為初始化只能初始化一次,而構造函數體內可以多次賦值

初始化列表的概念

初始化列表: 以一個冒號開始,接著是一個以逗號分隔的數據成員列表,每個"成員變量"后面跟一個放在括號中的初始值或表達式

Date(int year, int month, int day): _year(year), _month(month){_day = day}// 函數體里面能夠放數據
//Date(int year, int month, int day)
//{
//		_year = year;
//		_month = month;
//		_day = day;
//}

初始化列表的特征

使用初始化列表時注意:

  • 每個成員變量在初始化列表中只能出現一次(初始化只能初始化一次)
  • 類中包含以下成員,必須放在初始化列表位置進行初始化:
    • 引用成員變量
    • const成員變量
    • 自定義類型成員(且該類沒有默認構造函數時)

例如:

class A
{
public:A(int a):_a(a){}
private:int _a;
};class B
{
public:B(int a, int ref):_aobj(a),_ref(ref),_n(10){}
private:A _aobj; // 沒有默認構造函數int& _ref; // 引用const int _n; // const
};
int main()
{B bb(1,2);A aa(1);
}

在這里插入圖片描述

特征:
1. 盡量使用初始化列表初始化

因為不管你是否使用初始化列表,對于自定義類型成員變量,一定會先經過初始化列表初始化

2. 成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關

3. 當用戶沒有顯示傳參初始化時,編譯器會用用戶定義的缺省值

public:B(int a):b(a) // b = 1;{}
private:int b = 1;

explicit關鍵字

構造函數不僅能構造和初始化對象,對于單個參數或除第一個參數無默認值,其余均有默認值的構造函數,還有隱式類型轉換的作用,隱式類型轉換是在編程中編譯器自動進行的一種類型轉換方式

class pxt
{
public:explicit pxt(int a = 0):_a(a){cout << "pxt(int a)" << endl;}~pxt(){cout << "~pxt()" << endl;}
private:int _a;
};
int main()
{pxt a1 = 2024;// 用一個整形變量給自定義類型對象賦值// 編譯器會用2024構造一個無名對象,最后用無名對象給a1對象進行賦值// 正常情景是能賦值的,但是explicit修飾構造函數后,會禁止構造函數的隱式轉換return 0;
}

關鍵字explicit修飾構造函數,將會禁止構造函數的隱式轉換


4. static成員

static成員的概念

概念:

  • 聲明為static的類成員稱為類的靜態成員,
    static修飾的成員變量,稱之為靜態成員變量,
    static修飾的成員函數,稱之為靜態成員函數
  • 靜態成員變量一定要在類外進行初始化
class pxt
{
public:void Print(){cout << _a << endl;}
private:// 在類中聲明static int _a;//如果是靜態成員函數,則沒有this指針
};
// 在類外定義
int pxt:: _a = 100;int main()
{pxt A;A.Print();
}

static成員的特征

特性:

  • 靜態成員為所有類對象所共享,存放在靜態區
  • 靜態成員變量必須在類外定義,類中只是聲明
  • 類靜態成員可用 類名::靜態成員 或者 對象.靜態成員 來訪問
  • 靜態成員函數沒有隱藏的this指針,不能訪問任何非靜態成員
  • 靜態成員也是類的成員,受訪問限定符的限制

5. 友元

友元: 提供了一種突破封裝的方式,有時提供了便利。但是友元會增加耦合度,破壞了封裝,所以友元不宜多用。

友元分為:

  • 友元函數
  • 友元類

友元函數

如果嘗試去重載operator<<,我們發現沒辦法將operator<<重載成成員函數,因為函數的參數位置不一樣,cout的輸出流對象和隱含的this指針在搶占第一個參數的位置,重載operator>>同理
在這里插入圖片描述
d << cout; -> d.operator<<(&d, cout); 不符合常規調用
因為成員函數第一個參數一定是隱藏的this,所以d必須放在<<的左側

但是問題來了,如果我們寫成全局函數,又無法使用私有的成員變量,這時友元的作用就凸顯出來了!

友元函數: 可以直接訪問類的私有成員,它是定義在類外部的普通函數,不屬于任何類,但需要在類的內部聲明,聲明時需要加friend關鍵字

例如:

class Date
{// 不聲明友元,將無法調用私有成員friend ostream& operator<<(ostream& _out, const Date& d);
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}//void operator<< (ostream& _out)//{//	_out << _year << " " << _month << " " << _day;//}
private:int _year; // 年int _month; // 月int _day; // 日
};ostream& operator<< (ostream& _out, const Date& d)
{_out << d._year << " " << d._month << " " << d._day;return _out;
}int main()
{Date d(2024,5,23);cout << d << endl;// d << cout;
}

關于友元函數有以下幾點:

  • 友元函數可訪問類的私有和保護成員,但不是類的成員函數
  • 友元函數不能用const修飾
  • 友元函數可以在類定義的任何地方聲明,不受類訪問限定符限制
  • 一個函數可以是多個類的友元函數
  • 友元函數的調用與普通函數的調用原理相同

友元類

友元類的所有成員函數都可以是另一個類的友元函數,都可以訪問另一個類中的非公有成員

友元類的特征:
友元關系是單向的,不具有交換性
友元關系不能傳遞
如果C是B的友元, B是A的友元,則不能說明C時A的友元
友元關系不能繼承,在繼承位置再給大家詳細介紹

關于友元關系的單向性我舉個例子:

class A
{friend class B;
public:// ......
private:int _year;int _month;int _day;
};
class B
{
public:void test(int year, int month, int day){// 直接訪問A類私有的成員變量// 但是A 不能訪問B 中私有的成員變量_d._year = year;_d._month = month;_d._day = day;
}
private:int good;A _d;
};

B能直接訪問A類私有的成員變量,但是A 不能訪問B 中私有的成員變量


講到友元類,我們再來介紹一下一個跟友元類有很大關系的內部類
內部類

概念:如果一個類定義在另一個類的內部,這個內部類就叫做內部類。內部類是一個獨立的類,不屬于外部類,更不能通過外部類的對象去訪問內部類的成員。外部類對內部類沒有任何優越的訪問權限

注意:內部類就是外部類的友元類,內部類能訪問外部類中的所有成員,反之則不能!

class A
{
public:// ......A(int year = 2024, int month = 5, int day = 20): _year(year),_month(month),_day(day){}class B{public:void test(const A& _d){cout << _d._year << " " << _d._month << " " << _d._day << endl;}};
private:int _year;int _month;int _day;
};
int main()
{A::B b;b.test(A());
}

在這里插入圖片描述
內部類的特征

特性:

  • 內部類可以定義在外部類的所有成員
  • 內部類可以直接訪問外部類中的static成員,不需要外部類的對象/類名
  • sizeof(外部類)=外部類,和內部類沒有任何關系

6. 類的匿名對象

class pxt
{
public:pxt(int a = 0):_a(a){cout << "pxt(int a)" << endl;}~pxt(){cout << "~pxt()" << endl;}
private:int _a;
};
int main()
{// 但是我們可以這么定義匿名對象,匿名對象的特點不用取名字,// 但是他的生命周期只有這一行,我們可以看到下一行他就會自動調用析構函數// 匿名對象pxt();// 隱式類型轉換pxt a1 = 2024;return 0;
}

在這里插入圖片描述

  • 生命周期只有一行,會自動調用析構函數
  • 匿名對象的特點不用取名字

因此當我們只是想使用類中的某一個函數時,我們能創建匿名對象!


7. 總結

類和對象的所有內容已經了解完畢,類和對象在整個C++上都有舉足輕重的作用,大家千萬不要忽視,而類和對象的重點在四個成員函數上,下節我將學習C++的內存管理

謝謝大家支持本篇到這里就結束了,祝大家天天開心!
在這里插入圖片描述


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

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

相關文章

Java的函數式接口和 Lambda 表達式

在 Java 8 中&#xff0c;可以通過使用函數式接口和 Lambda 表達式來實現類似 JavaScript 中將函數作為參數傳遞的功能。 以下是一個簡單的示例&#xff0c;演示如何在 Java 中使用函數式接口將函數作為參數傳遞&#xff1a; 定義一個函數式接口&#xff08;函數式接口是只有…

CentOS上升級glibc2.17至glibc2.31

glibc是Linux系統中的重要組件之一。在CentOS中&#xff0c;glibc通常是作為系統的默認C標準庫使用的&#xff0c;因為它是許多軟件的基礎庫。在CentOS中&#xff0c;glibc的版本通常與CentOS版本一起發布。因為CentOS通常會優先選擇穩定性而不是最新性&#xff0c;所以CentOS使…

Vue項目如何進行XSS防護

前言 在目前主推網絡安全的情況下&#xff0c;很多開發項目都需要在上線前進行滲透測試&#xff0c;當符合滲透測試標準及沒有安全漏洞即可正常上線&#xff0c;當前還會有代碼審計的&#xff0c;這個另當別論。 如何對XSS進行防護 在很多的富文本編輯器項目中&#xff0c;x…

leecode熱題100---994:腐爛的橘子

題目&#xff1a; 在給定的 m x n 網格 grid 中&#xff0c;每個單元格可以有以下三個值之一&#xff1a; 值 0 代表空單元格&#xff1b; 值 1 代表新鮮橘子&#xff1b; 值 2 代表腐爛的橘子。 每分鐘&#xff0c;腐爛的橘子 周圍 4 個方向上相鄰 的新鮮橘子都會腐爛。 返回…

C++之第九課

課程列表 今天&#xff0c;我們要學習一種結構&#xff1a;循環結構。 循環的方法有3種。 今天先將第1種for學了&#xff1a; int a;//循環變量 int b; for(a1;a<10;a){//像if那樣“打包”cout<<a<<" ";b; } 當然&#xff0c;也可以這樣寫&#…

【MySQL精通之路】InnoDB(5)-內存結構

總目錄&#xff1a; 【MySQL精通之路】InnoDB存儲引擎-CSDN博客 上一篇&#xff1a; 【MySQL精通之路】InnoDB(4)-架構圖-CSDN博客 目錄 ?編輯 1 緩存池&#xff08;Buffer Pool&#xff09; 1.1 緩存池LRU算法 1.2 緩存區配置 1.3 使用InnoDB標準監視器監視緩存池 …

SSRF服務端請求偽造漏洞原理與修復及靶場實踐

SSRF服務端請求偽造漏洞原理與修復及靶場實踐 SSRF漏洞原理與檢測 SSRF&#xff08;Server-Side Request Forgery&#xff0c;服務器端請求偽造&#xff09;漏洞是一種因為服務端提供了遠程訪問服務&#xff0c;而并未對請求目標進行限制或限制不嚴格而引起的安全漏洞&#x…

Java Apache Jexl規則引擎初體驗

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、模板引擎的選擇&#xff1f;二、什么是JEXL規則引擎&#xff1f;優點缺點 三、其他規則引擎四、示例1.引入依賴2.方法示例3、代碼解釋4、效果![import java…

VMware虛擬機Ubuntu 22.04.4 LTS系統 NAT網絡設置異常解決

現象&#xff1a; 近日&#xff0c;一直工作正常的虛擬機莫名出現網絡無法連接的情況。 參考網上的各種教程&#xff0c;終于解決問題。 如遇到類似情況的&#xff0c;可以嘗試這個方式&#xff0c;看能否解決問題。 網絡連接&#xff1a;采用NAT模式 異常&#xff1a;網絡圖標…

C++數據結構——哈希表

前言&#xff1a;本篇文章將繼續進行C數據結構的講解——哈希表。 目錄 一.哈希表概念 二.哈希函數 1.除留取余法 三.哈希沖突 1.閉散列 線性探測 &#xff08;1&#xff09;插入 &#xff08;2&#xff09;刪除 2. 開散列 開散列概念 四.閉散列哈希表 1.基本框架 …

場內期權怎么開戶?傭金手續費最低是多少?

今天期權懂帶你了解場內期權怎么開戶&#xff1f;傭金手續費最低是多少&#xff1f;我國的首個場內期權是50ETF期權&#xff0c;隨著投資者對期權產品日漸熟悉&#xff0c;投資者參與數量與交易量穩步增長。 場內期權怎么開戶&#xff1f; 滿足資金要求&#xff1a;根據監管要…

自動打卡腳本

奕輔導自動打卡腳本 打卡腳本&#xff0c;使用前需手動打卡一次并需要抓包&#xff0c;在其中找到相關的token。 # -*- encoding:utf-8 -*-import requests import jsonpunch_in_data {"questionnairePublishEntityId": "1001640744275339000980000000001&qu…

MyBatis:Parameter Maps collection does not contain value for 報錯解決收錄

MyBatis&#xff1a;Parameter Maps collection does not contain value for 報錯問題解決收錄 1.報錯收錄 后端測試時偶然遇到的用mybatis生成好的mapper文件&#xff0c;報Result Maps collection does not contain value…的錯誤 2.報錯分析 java.lang.ILledalAraumentEx…

必應bing國內廣告開戶首充和開戶費是多少?

微軟必應Bing作為國內領先的搜索引擎之一&#xff0c;其廣告平臺憑借其精準的投放、高效的數據分析和廣泛的用戶覆蓋&#xff0c;已成為眾多企業的首選。 根據最新政策&#xff0c;2024年必應Bing國內廣告開戶預充值金額設定為1萬元人民幣起。這一調整旨在確保廣告主在賬戶初始…

【嵌入式DIY實例】-OLED顯示DHT22傳感器數據

OLED顯示DHT22傳感器數據 1、應用實例介紹 本次實例將演示如何在OLED中顯示DHT22溫度濕度傳感器的數據。實例主要分兩步來完成: DHT22傳感器驅動,采集溫度和濕度OLED驅動,顯示采集到的溫度值和濕度值。在前面的文章中,對OLED的應用和驅動做了介紹,請參考: ESP8266-Ardu…

1.Nginx上配置 HTTPS

1.安裝 Nginx&#xff1a; 如果還沒有安裝 Nginx&#xff0c;可以使用包管理工具安裝。例如&#xff0c;在 Ubuntu 上&#xff1a; sudo apt update sudo apt install nginx2.上傳證書和私鑰文件&#xff1a; 將你的證書文件和私鑰文件上傳到服務器上的某個目錄&#xff0c;…

VBA宏指令寫的方法突然不能用了

背景:項目組有個自動化測試項目,實在excel中利用VBA開發的;時間比較久遠,我前面的哥們走后,這個軟件一直在用,最近系統不知道是不是更新的緣故;有些代碼除了問題; 先上源碼: Dim Conn As Object, Rst As Object Dim sqlStr$ Dim str_start_SN$, str2$ str_start_SN …

python 線性回歸模型

教材鏈接-3.2. 線性回歸的從零開始實現 c實現 該博客僅用于記錄一下自己的代碼&#xff0c;可與c實現作為對照 from d2l import torch as d2l import torch import random # nn是神經網絡的縮寫 from torch import nn from torch.utils import data# 加載訓練數據 # 加載訓…

什么是網關,網關有哪些作用?

網關(Gateway)是在計算機網絡中用于連接兩個獨立的網絡的設備&#xff0c;它能夠在兩個不同協議的網絡之間傳遞數據。在互聯網中&#xff0c;網關是一個可以連接不同協議的網絡的設備&#xff0c;比如說可以連接局域網和互聯網&#xff0c;它可以把局域網 的內部網絡地址轉換成…

論文閱讀--GLIP

把detection和phrase ground(對于給定的sentence&#xff0c;要定位其中提到的全部物體)這兩個任務合起來變成統一框架&#xff0c;從而擴展數據來源&#xff0c;因為文本圖像對的數據還是很好收集的 目標檢測的loss是分類loss定位loss&#xff0c;它與phrase ground的定位los…