C++類與對(duì)象的重點(diǎn)知識(shí)點(diǎn)詳細(xì)分析
一、類的六個(gè)默認(rèn)成員函數(shù)詳解
我們這里接著C++類與對(duì)象的基礎(chǔ)知識(shí)點(diǎn)詳細(xì)分析來寫。C++類和對(duì)象概念及實(shí)現(xiàn)詳解上篇中我們重點(diǎn)解釋了類的儲(chǔ)存方式、類和對(duì)象的概念、this指針、構(gòu)造函數(shù)、析構(gòu)函數(shù)。這篇文章我們主要對(duì)剩余的四個(gè)默認(rèn)成員函數(shù)拷貝構(gòu)造、賦值運(yùn)算符重載、普通對(duì)象取地址、const對(duì)象取地址,以及運(yùn)算符重載、友元函數(shù)、static成員等重點(diǎn)內(nèi)容進(jìn)行詳解。
1、拷貝構(gòu)造
1.1 拷貝構(gòu)造的引入及概念
我們想象一下:在創(chuàng)建對(duì)象時(shí),可否創(chuàng)建一個(gè)與已存在對(duì)象一摸一樣的新對(duì)象呢?答案是可以的。那怎么創(chuàng)建一個(gè)一樣的新對(duì)象呢?是自己對(duì)新對(duì)象初始化嗎?其實(shí)是不用的。C++在這里提供了一個(gè)默認(rèn)的成員函數(shù),也就是拷貝構(gòu)造,來完成創(chuàng)建一個(gè)新的一摸一樣的對(duì)象。我們先來看一下拷貝構(gòu)造的概念。
拷貝構(gòu)造函數(shù):只有單個(gè)形參,該形參是對(duì)本類類型對(duì)象的引用(一般常用const修飾),在用已存在的類類型對(duì)象創(chuàng)建新對(duì)象時(shí)由編譯器自動(dòng)調(diào)用。
1.2 拷貝構(gòu)造函數(shù)的特征
拷貝構(gòu)造函數(shù)也是特殊的成員函數(shù),其特征如下:
- 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式。
- 拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須是類類型對(duì)象的引用,使用傳值方式編譯器直接報(bào)錯(cuò),因?yàn)闀?huì)引發(fā)無窮遞歸調(diào)用。
- 若未顯式定義,編譯器會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。 默認(rèn)的拷貝構(gòu)造函數(shù)對(duì)象按內(nèi)存存儲(chǔ)按字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝。
- 編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)已經(jīng)可以完成字節(jié)序的值拷貝了,但是要注意的是類中如果沒有涉及資源申請(qǐng)時(shí),拷貝構(gòu)造函數(shù)是否寫都可以;一旦涉及到資源申請(qǐng)時(shí),則拷貝構(gòu)造函數(shù)是一定要寫的,否則就是淺拷貝。
- 拷貝構(gòu)造函數(shù)典型調(diào)用場(chǎng)景:使用已存在對(duì)象創(chuàng)建新對(duì)象、函數(shù)參數(shù)類型為類類型對(duì)象、函數(shù)返回值類型為類類型對(duì)象。
特征2中提到,形參必須是類類型對(duì)象的引用,使用傳值的話為什么會(huì)引發(fā)我窮遞歸呢?因?yàn)槲覀儌鞯膶?shí)參是自定義類,傳值的形參初始化是實(shí)參調(diào)用拷貝構(gòu)造函數(shù)完成的。下一個(gè)拷貝構(gòu)造函數(shù)形參初始化又要調(diào)用拷貝構(gòu)造函數(shù),所以會(huì)無限遞歸。
特征3需要注意的是:在編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)中,內(nèi)置類型是按照字節(jié)方式直接拷貝的,而自定義類型是調(diào)用其拷貝構(gòu)造函數(shù)完成拷貝的。我們結(jié)合著以下代碼理解一下。
class Time { public: Time() { _hour = 1; _minute = 1; _second = 1; } Time(const Time& t) { _hour = t._hour; _minute = t._minute; _second = t._second; cout << "Time::Time(const Time&)" << endl; } private: int _hour; int _minute; int _second; }; class Date { private: // 基本類型(內(nèi)置類型) int _year = 1970; int _month = 1; int _day = 1; // 自定義類型 Time _t; }; int main() { Date d1; // 用已經(jīng)存在的d1拷貝構(gòu)造d2,此處會(huì)調(diào)用Date類的拷貝構(gòu)造函數(shù) // 但Date類并沒有顯式定義拷貝構(gòu)造函數(shù),則編譯器會(huì)給Date類生成一個(gè)默認(rèn)的拷貝構(gòu) // 造函數(shù),_t為自定義類型,會(huì)自動(dòng)調(diào)用自己的構(gòu)造函數(shù)完成拷貝 Date d2(d1); return 0; }
2、賦值運(yùn)算符重載
2.1 運(yùn)算符重載
在學(xué)習(xí)賦值運(yùn)算符重載時(shí),我們先學(xué)習(xí)一下運(yùn)算符重載。
C++為了增強(qiáng)代碼的可讀性引入了運(yùn)算符重載,運(yùn)算符重載是具有特殊函數(shù)名的函數(shù),也具有其返回值類型,函數(shù)名字以及參數(shù)列表,其返回值類型與參數(shù)列表與普通的函數(shù)類似。
函數(shù)名字為:關(guān)鍵字operator后面接需要重載的運(yùn)算符符號(hào)。 函數(shù)原型:返回值類型 operator操作符(參數(shù)列表)。
運(yùn)算符重載有五個(gè)需要注意的點(diǎn):
- 不能通過連接其他符號(hào)來創(chuàng)建新的操作符:比如operator@;
- 重載操作符必須有一個(gè)類類型參數(shù);
- 用于內(nèi)置類型的運(yùn)算符,其含義不能改變,例如:內(nèi)置的整型+,不能改變其含義;
- 作為類成員函數(shù)重載時(shí),其形參看起來比操作數(shù)數(shù)目少1,因?yàn)槌蓡T函數(shù)的第一個(gè)參數(shù)為隱藏的this;
- .* :: sizeof ?: . 注意以上5個(gè)運(yùn)算符不能重載。
大家可結(jié)合下面的例子來理解一下運(yùn)算符重載。
class Date { public: Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } // bool operator==(Date* this, const Date& d2) // 這里需要注意的是,左操作數(shù)是this,指向調(diào)用函數(shù)的對(duì)象 bool operator==(const Date& d2) { return _year == d2._year; && _month == d2._month && _day == d2._day; } private: int _year; int _month; int _day; }; void Test () { Date d1(2022, 12, 26); Date d2(2022, 12, 27); cout<<(d1 == d2)<<endl; }
2.2 賦值運(yùn)算符重載
賦值運(yùn)算符重載格式:
- 參數(shù)類型:const T&,傳遞引用可以提高傳參效率;
- 返回值類型:T&,返回引用可以提高返回的效率,有返回值目的是為了支持連續(xù)賦值;
- 檢測(cè)是否自己給自己賦值;
- 返回*this :要復(fù)合連續(xù)賦值的含義。
結(jié)合下面代碼理解。
class Date { public : Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } Date (const Date& d) { _year = d._year; _month = d._month; _day = d._day; } Date& operator=(const Date& d) { if(this != &d)//判斷是否自己給自己復(fù)制 { _year = d._year; _month = d._month; _day = d._day; } return *this; } private: int _year ; int _month ; int _day ; };
這里還有需要注意的是2賦值運(yùn)算符只能重載成類的成員函數(shù)不能重載成全局函數(shù)。為什么呢?原因是賦值運(yùn)算符如果在類中不顯式實(shí)現(xiàn),編譯器會(huì)生成一個(gè)默認(rèn)的。此時(shí)用戶再在類外自己實(shí)現(xiàn)一個(gè)全局的賦值運(yùn)算符重載,就和編譯器在類中生成的默認(rèn)賦值運(yùn)算符重載沖突了,故賦值運(yùn)算符重載只能是類的成員函數(shù)。
用戶沒有顯式實(shí)現(xiàn)時(shí),編譯器會(huì)生成一個(gè)默認(rèn)賦值運(yùn)算符重載,以值的方式逐字節(jié)拷貝(淺拷貝)。注意:內(nèi)置類型成員變量是直接賦值的,而自定義類型成員變量需要調(diào)用對(duì)應(yīng)類的賦值運(yùn)算符重載完成賦值。
3、普通對(duì)象取地址
普通對(duì)象取地址默認(rèn)成員函數(shù)一般不用重新定義 ,編譯器默認(rèn)會(huì)生成。一般編譯器自動(dòng)生成的基本上會(huì)滿足我們的需求。理解起來也很簡(jiǎn)單,我們直接看代碼。
class Date { public : Date* operator&() { return this ; } private : int _year ; // 年 int _month ; // 月 int _day ; // 日 };
我們這里直接返回this指針即可。
4、const對(duì)象取地址
const對(duì)象取地址與普通對(duì)象取地址大同小異,const修飾的對(duì)象內(nèi)容不可被更改,所以我們傳址的時(shí)候需要加上const修飾形參指針。我們直接看代碼。
class Date { public : const Date* operator&()const { return this ; } private : int _year ; // 年 int _month ; // 月 int _day ; // 日 };
普通對(duì)象取地址、const對(duì)象取地址這兩個(gè)運(yùn)算符一般不需要重載,使用編譯器生成要重載,比如想讓別人獲取到指定的內(nèi)容(返回空指針)!
二、類和對(duì)象重點(diǎn)知識(shí)點(diǎn)
1、初始化列表
class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; };
雖然上述構(gòu)造函數(shù)調(diào)用之后,對(duì)象中已經(jīng)有了一個(gè)初始值,但是不能將其稱為對(duì)對(duì)象中成員變量的初始化,構(gòu)造函數(shù)體中的語句只能將其稱為賦初值,而不能稱作初始化。因?yàn)槌跏蓟荒艹跏蓟淮危鴺?gòu)造函數(shù)體內(nèi)可以多次賦值
那對(duì)象的成員變量是在哪里初始化的呢?是在構(gòu)造函數(shù)的初始化列表進(jìn)行的。
初始化列表:以一個(gè)冒號(hào)開始,接著是一個(gè)以逗號(hào)分隔的數(shù)據(jù)成員列表,每個(gè)"成員變量"后面跟一個(gè)放在括號(hào)中的初始值或表達(dá)式。我們結(jié)合下面代碼理解。
class Date { public: Date(int year, int month, int day) : _year(year) , _month(month) , _day(day) {} private: int _year; int _month; int _day; };
初始化列表需要注意的幾點(diǎn):
- 每個(gè)成員變量在初始化列表中只能出現(xiàn)一次(初始化只能初始化一次);
- 引用成員變量、const成員變量、自定義類型成員(且該類沒有默認(rèn)構(gòu)造函數(shù)時(shí)) ,這三個(gè)成員變量必須放在初始化列表位置進(jìn)行初始化;
- 盡量使用初始化列表初始化,因?yàn)椴还苣闶欠袷褂贸跏蓟斜恚瑢?duì)于自定義類型成員變量,一定會(huì)先使用初始化列表初始化。
- 成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關(guān)。
2、static成員
聲明為static的類成員稱為類的靜態(tài)成員,用static修飾的成員變量,稱之為靜態(tài)成員變量;用static修飾的成員函數(shù),稱之為靜態(tài)成員函數(shù)。靜態(tài)成員變量一定要在類外進(jìn)行初始化
static修飾特征:
- 靜態(tài)成員為所有類對(duì)象所共享,不屬于某個(gè)具體的對(duì)象,存放在靜態(tài)區(qū);
- 靜態(tài)成員變量必須在類外定義,定義時(shí)不添加static關(guān)鍵字,類中只是聲明;
- 類靜態(tài)成員即可用類名::靜態(tài)成員 或者對(duì)象.靜態(tài)成員來訪問;
- 靜態(tài)成員函數(shù)沒有隱藏的this指針,不能訪問任何非靜態(tài)成員;
- 靜態(tài)成員也是類的成員,受public、protected、private 訪問限定符的限制。
3、友元函數(shù)
我們都知道在類的外面是不能訪問私有成員的。那要是我們?cè)陬愅饷娑x的函數(shù)必須要調(diào)用類的私有成員呢?這里C++提供了友元函數(shù)。
友元函數(shù)可以直接訪問類的私有成員,它是定義在類外部的普通函數(shù),不屬于任何類,但需要在類的內(nèi)部聲明,聲明時(shí)需要加friend關(guān)鍵字。
例如當(dāng)我們實(shí)現(xiàn)運(yùn)算符'>>'和‘<<’的重載時(shí)就使用到了友元函數(shù),我們看下面代碼。
class Date { friend ostream& operator<<(ostream& _cout, const Date& d); friend istream& operator>>(istream& _cin, Date& d); public: Date(int year = 1900, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} private: int _year; int _month; int _day; }; ostream& operator<<(ostream& _cout, const Date& d) { _cout << d._year << "-" << d._month << "-" << d._day; return _cout; } istream& operator>>(istream& _cin, Date& d) { _cin >> d._year; _cin >> d._month; _cin >> d._day; return _cin; } int main() { Date d; cin >> d; cout << d << endl; return 0; }
友元函數(shù)說明:
- 友元函數(shù)可訪問類的私有和保護(hù)成員,但不是類的成員函數(shù);
- 友元函數(shù)不能用const修飾;
- 友元函數(shù)可以在類定義的任何地方聲明,不受類訪問限定符限制;
- 一個(gè)函數(shù)可以是多個(gè)類的友元函數(shù);
- 友元函數(shù)的調(diào)用與普通函數(shù)的調(diào)用原理相同。
4、友元類
友元類的所有成員函數(shù)都可以是另一個(gè)類的友元函數(shù),都可以訪問另一個(gè)類中的非公有成員。
- 友元關(guān)系是單向的,不具有交換性。
- 比如上述Time類和Date類,在Time類中聲明Date類為其友元類,那么可以在Date類中直接
- 訪問Time類的私有成員變量,但想在Time類中訪問Date類中私有的成員變量則不行。
- 友元關(guān)系不能傳遞
- 如果C是B的友元, B是A的友元,則不能說明C時(shí)A的友元。
class Time { friend class Date; // 聲明日期類為時(shí)間類的友元類,則在日期類中就直接訪問Time類 // 中的私有成員變量 public: Time(int hour = 0, int minute = 0, int second = 0) : _hour(hour) , _minute(minute) , _second(second) {} private: int _hour; int _minute; int _second; }; class Date { public: Date(int year = 1900, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} void SetTimeOfDate(int hour, int minute, int second) { // 直接訪問時(shí)間類私有的成員變量 _t._hour = hour; _t._minute = minute; _t._second = second; } private: int _year; int _month; int _day; Time _t; };
總結(jié)
類和對(duì)象細(xì)節(jié)較多,也是相對(duì)來叔十分重要的一部分,需要我們重點(diǎn)但掌握六個(gè)默認(rèn)成員函數(shù)、類和對(duì)象的基本定義、this指針、static成員、初始化列表、友元函數(shù)和友元類。反復(fù)學(xué)習(xí)達(dá)到熟能生巧。
到此這篇關(guān)于C++類與對(duì)象的重點(diǎn)知識(shí)點(diǎn)詳細(xì)分析的文章就介紹到這了,更多相關(guān)C++類與對(duì)象內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言鏈表實(shí)現(xiàn)銷售管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言鏈表實(shí)現(xiàn)銷售管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02C++友元(Friend)用法實(shí)例簡(jiǎn)介
這篇文章主要介紹了C++友元(Friend)用法,對(duì)于C++的學(xué)習(xí)來說有很好的參考價(jià)值,需要的朋友可以參考下2014-08-08C++ 非遞歸實(shí)現(xiàn)二叉樹的前中后序遍歷
本文將結(jié)合動(dòng)畫和代碼演示如何通過C++ 非遞歸實(shí)現(xiàn)二叉樹的前中后序的遍歷,代碼具有一定的價(jià)值,感興趣的同學(xué)可以學(xué)習(xí)一下2021-11-11Window10下安裝VS2022社區(qū)版的實(shí)現(xiàn)步驟(圖文教程)
很多和同學(xué)們?cè)诮佑|c語言的時(shí)候都是使用VS,本文主要介紹了Window10下如何安裝VS2022社區(qū)版的實(shí)現(xiàn)步驟,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02c語言中exit和return的區(qū)別點(diǎn)總結(jié)
小編今天給大家整理了關(guān)于c語言中exit和return的不同點(diǎn)及相關(guān)基礎(chǔ)知識(shí)點(diǎn),有興趣的朋友們可以跟著學(xué)習(xí)下。2021-10-10