C++類與對象的重點知識點詳細分析
一、類的六個默認成員函數(shù)詳解
我們這里接著C++類與對象的基礎知識點詳細分析來寫。C++類和對象概念及實現(xiàn)詳解上篇中我們重點解釋了類的儲存方式、類和對象的概念、this指針、構造函數(shù)、析構函數(shù)。這篇文章我們主要對剩余的四個默認成員函數(shù)拷貝構造、賦值運算符重載、普通對象取地址、const對象取地址,以及運算符重載、友元函數(shù)、static成員等重點內容進行詳解。
1、拷貝構造
1.1 拷貝構造的引入及概念
我們想象一下:在創(chuàng)建對象時,可否創(chuàng)建一個與已存在對象一摸一樣的新對象呢?答案是可以的。那怎么創(chuàng)建一個一樣的新對象呢?是自己對新對象初始化嗎?其實是不用的。C++在這里提供了一個默認的成員函數(shù),也就是拷貝構造,來完成創(chuàng)建一個新的一摸一樣的對象。我們先來看一下拷貝構造的概念。
拷貝構造函數(shù):只有單個形參,該形參是對本類類型對象的引用(一般常用const修飾),在用已存在的類類型對象創(chuàng)建新對象時由編譯器自動調用。
1.2 拷貝構造函數(shù)的特征
拷貝構造函數(shù)也是特殊的成員函數(shù),其特征如下:
- 拷貝構造函數(shù)是構造函數(shù)的一個重載形式。
- 拷貝構造函數(shù)的參數(shù)只有一個且必須是類類型對象的引用,使用傳值方式編譯器直接報錯,因為會引發(fā)無窮遞歸調用。
- 若未顯式定義,編譯器會生成默認的拷貝構造函數(shù)。 默認的拷貝構造函數(shù)對象按內存存儲按字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝。
- 編譯器生成的默認拷貝構造函數(shù)已經(jīng)可以完成字節(jié)序的值拷貝了,但是要注意的是類中如果沒有涉及資源申請時,拷貝構造函數(shù)是否寫都可以;一旦涉及到資源申請時,則拷貝構造函數(shù)是一定要寫的,否則就是淺拷貝。
- 拷貝構造函數(shù)典型調用場景:使用已存在對象創(chuàng)建新對象、函數(shù)參數(shù)類型為類類型對象、函數(shù)返回值類型為類類型對象。
特征2中提到,形參必須是類類型對象的引用,使用傳值的話為什么會引發(fā)我窮遞歸呢?因為我們傳的實參是自定義類,傳值的形參初始化是實參調用拷貝構造函數(shù)完成的。下一個拷貝構造函數(shù)形參初始化又要調用拷貝構造函數(shù),所以會無限遞歸。
特征3需要注意的是:在編譯器生成的默認拷貝構造函數(shù)中,內置類型是按照字節(jié)方式直接拷貝的,而自定義類型是調用其拷貝構造函數(shù)完成拷貝的。我們結合著以下代碼理解一下。
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:
// 基本類型(內置類型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定義類型
Time _t;
};
int main()
{
Date d1;
// 用已經(jīng)存在的d1拷貝構造d2,此處會調用Date類的拷貝構造函數(shù)
// 但Date類并沒有顯式定義拷貝構造函數(shù),則編譯器會給Date類生成一個默認的拷貝構
// 造函數(shù),_t為自定義類型,會自動調用自己的構造函數(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ù);
- 用于內置類型的運算符,其含義不能改變,例如:內置的整型+,不能改變其含義;
- 作為類成員函數(shù)重載時,其形參看起來比操作數(shù)數(shù)目少1,因為成員函數(shù)的第一個參數(shù)為隱藏的this;
- .* :: sizeof ?: . 注意以上5個運算符不能重載。
大家可結合下面的例子來理解一下運算符重載。
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,指向調用函數(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ù)賦值的含義。
結合下面代碼理解。
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é)拷貝(淺拷貝)。注意:內置類型成員變量是直接賦值的,而自定義類型成員變量需要調用對應類的賦值運算符重載完成賦值。
3、普通對象取地址
普通對象取地址默認成員函數(shù)一般不用重新定義 ,編譯器默認會生成。一般編譯器自動生成的基本上會滿足我們的需求。理解起來也很簡單,我們直接看代碼。
class Date
{
public :
Date* operator&()
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};我們這里直接返回this指針即可。
4、const對象取地址
const對象取地址與普通對象取地址大同小異,const修飾的對象內容不可被更改,所以我們傳址的時候需要加上const修飾形參指針。我們直接看代碼。
class Date
{
public :
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};普通對象取地址、const對象取地址這兩個運算符一般不需要重載,使用編譯器生成要重載,比如想讓別人獲取到指定的內容(返回空指針)!
二、類和對象重點知識點
1、初始化列表
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};雖然上述構造函數(shù)調用之后,對象中已經(jīng)有了一個初始值,但是不能將其稱為對對象中成員變量的初始化,構造函數(shù)體中的語句只能將其稱為賦初值,而不能稱作初始化。因為初始化只能初始化一次,而構造函數(shù)體內可以多次賦值
那對象的成員變量是在哪里初始化的呢?是在構造函數(shù)的初始化列表進行的。
初始化列表:以一個冒號開始,接著是一個以逗號分隔的數(shù)據(jù)成員列表,每個"成員變量"后面跟一個放在括號中的初始值或表達式。我們結合下面代碼理解。
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成員變量、自定義類型成員(且該類沒有默認構造函數(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ù)必須要調用類的私有成員呢?這里C++提供了友元函數(shù)。
友元函數(shù)可以直接訪問類的私有成員,它是定義在類外部的普通函數(shù),不屬于任何類,但需要在類的內部聲明,聲明時需要加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ù)的調用與普通函數(shù)的調用原理相同。
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é)較多,也是相對來叔十分重要的一部分,需要我們重點但掌握六個默認成員函數(shù)、類和對象的基本定義、this指針、static成員、初始化列表、友元函數(shù)和友元類。反復學習達到熟能生巧。
到此這篇關于C++類與對象的重點知識點詳細分析的文章就介紹到這了,更多相關C++類與對象內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
QT連接Mysql數(shù)據(jù)庫的實現(xiàn)步驟
本文主要介紹了QT連接Mysql數(shù)據(jù)庫的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-06-06

