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