国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載)

這篇具有很好參考價(jià)值的文章主要介紹了【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)


目錄

一. 前言?

二. 默認(rèn)成員函數(shù)

三. 構(gòu)造函數(shù)

3.1 概念

3.2 特性

四. 析構(gòu)函數(shù)

4.1 概念

4.2 特性

五. 拷貝構(gòu)造函數(shù)

5.1 概念

5.2 特性

六. 運(yùn)算符重載

6.1 引入

6.2 概念

6.3 注意事項(xiàng)

6.4 重載示例

6.5 賦值運(yùn)算符重載

6.6 前置++和后置++運(yùn)算符重載

七. const成員函數(shù)

7.1 問(wèn)題引入

7.2 定義方式

7.3 使用細(xì)則

?八. 取地址運(yùn)算符重載


一. 前言?

? ? ? ? 上期我們介紹了一些關(guān)于類(lèi)的基礎(chǔ)知識(shí),學(xué)會(huì)了如何定義一個(gè)類(lèi),體會(huì)到了面向?qū)ο笾?span style="color:#fe2c24;">封裝的特征。本期我們將繼續(xù)類(lèi)和對(duì)象的學(xué)習(xí),重點(diǎn)討論C++類(lèi)中的成員函數(shù),并在下期我們將自己動(dòng)手實(shí)現(xiàn)一個(gè)類(lèi)----日期類(lèi)

? ? ? ? 話(huà)不多說(shuō),上菜咯!?。?/strong>

二. 默認(rèn)成員函數(shù)

? ? ? ? 如果一個(gè)類(lèi)中什么成員都沒(méi)有,我們將其稱(chēng)之為空類(lèi)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

//空類(lèi)
class Date
{

};

?????????但是空類(lèi)中真的什么都沒(méi)有嗎?實(shí)則不然。任何類(lèi)在什么都不寫(xiě)時(shí),編譯器會(huì)自動(dòng)生成6個(gè)默認(rèn)成員函數(shù)。默認(rèn)成員函數(shù):用戶(hù)沒(méi)有顯式實(shí)現(xiàn),編譯器會(huì)自動(dòng)生成的成員函數(shù)。如下所示:

【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

? ? ? ? 接下來(lái)的內(nèi)容,我們就對(duì)這6個(gè)默認(rèn)成員函數(shù)進(jìn)行逐一分析【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

三. 構(gòu)造函數(shù)

3.1 概念

? ? ? ? 我們來(lái)看看下面的日期類(lèi):

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Init(2022, 7, 5);
	d1.Print();
	Date d2;
	d2.Init(2022, 7, 6);
	d2.Print();
	return 0;
}

????????對(duì)于上面Date類(lèi),我們發(fā)現(xiàn)我們每次創(chuàng)建一個(gè)對(duì)象后,都要通過(guò)Init 方法給對(duì)象設(shè)置日期,這未免顯得過(guò)于麻煩,那能否在對(duì)象創(chuàng)建時(shí),就同步將信息設(shè)置進(jìn)去呢?

? ? ? ? 使用構(gòu)造函數(shù)就能很好的進(jìn)行解決。構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),函數(shù)名與類(lèi)名相同,創(chuàng)建類(lèi)對(duì)象時(shí)由編譯器自動(dòng)調(diào)用,以保證每個(gè)數(shù)據(jù)成員都有一個(gè)合適的初始值,并且在對(duì)象整個(gè)生命周期內(nèi)只調(diào)用一次。其形式如下:

class Date
{
public:
	//Date的構(gòu)造函數(shù)
	Date()
	{
		//進(jìn)行初始化
        //...
	}
};

3.2 特性

????????構(gòu)造函數(shù)是特殊的成員函數(shù),需要注意的是,構(gòu)造函數(shù)雖然名為構(gòu)造,但是其主要任務(wù)并不是創(chuàng)建對(duì)象開(kāi)辟空間,而是初始化對(duì)象。

? ? ? ? 構(gòu)造函數(shù)有如下特征

  1. 函數(shù)名與類(lèi)名相同
  2. 沒(méi)有返回值,void也不能有:
    class Date
    {
    public:
    	//Date的構(gòu)造函數(shù)
    	Date()
    	{
    		
    	}
    	void Date(){} //錯(cuò)誤寫(xiě)法,沒(méi)有返回值
    };
  3. 對(duì)象實(shí)例化時(shí)編譯器會(huì)自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)
  4. 構(gòu)造函數(shù)支持重載,可以匹配不同的初始化信息。從參數(shù)來(lái)看,主要分為無(wú)參構(gòu)造函數(shù)帶參構(gòu)造函數(shù)
    class Date
    {
    public:
    	// 1.無(wú)參構(gòu)造函數(shù)
    	Date()
    	{}
    	// 2.帶參構(gòu)造函數(shù)
    	Date(int year, int month, int day)
    	{
    		_year = year; 
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	//1.創(chuàng)建對(duì)象
    	//2.調(diào)用相應(yīng)的構(gòu)造函數(shù)
    	Date d1; //調(diào)用無(wú)參構(gòu)造函數(shù)
    	Date d2(2023, 8, 22); //調(diào)用帶參構(gòu)造函數(shù)
    }

    需要注意的是,調(diào)用無(wú)參的構(gòu)造函數(shù)時(shí),對(duì)象后面無(wú)需帶(),否則會(huì)變成函數(shù)聲明:

    Date d1; //調(diào)用無(wú)參構(gòu)造函數(shù)
    
    Date d3(); //聲明一個(gè)沒(méi)有形參的函數(shù)d3,它的返回值類(lèi)型為Date
  5. 構(gòu)造函數(shù)是默認(rèn)成員函數(shù)。如果類(lèi)中沒(méi)有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù),一旦用戶(hù)顯式定義編譯器將不再生成【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    class Date
    {
    public:
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	Date d1; //調(diào)用編譯器自動(dòng)生成的默認(rèn)構(gòu)造函數(shù),默認(rèn)構(gòu)造是無(wú)參的,相匹配
    
        Date d2(2023, 8, 22); //該行代碼會(huì)報(bào)錯(cuò),沒(méi)有匹配的帶參構(gòu)造函數(shù)
    }

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)而如果我們顯式地定義了構(gòu)造函數(shù),編譯器就不會(huì)自動(dòng)生成無(wú)參的默認(rèn)構(gòu)造函數(shù),如下:

    class Date
    {
    public:
    	//顯式定義帶參的構(gòu)造函數(shù)
    	Date(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	Date d1;  //該行代碼會(huì)報(bào)錯(cuò),沒(méi)有匹配的默認(rèn)構(gòu)造函數(shù)
    
    	Date d2(2023, 8, 22); //調(diào)用帶參的構(gòu)造函數(shù)
    }

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

  6. 編譯器自動(dòng)生成的默認(rèn)構(gòu)造函數(shù)對(duì)內(nèi)置類(lèi)型不會(huì)進(jìn)行初始化,如:int,char,double等等;而對(duì)于自定義類(lèi)型,會(huì)去調(diào)用該自定義類(lèi)型的默認(rèn)構(gòu)造函數(shù)。

    class Time
    {
    public:
    	Time() //Time類(lèi)的默認(rèn)構(gòu)造函數(shù)
    	{
    		_hours = 0;
    		_minute = 0;
    		_second = 0;
    	}
    private:
    	int _hours;
    	int _minute;
    	int _second;
    };
    class Date
    {
    public:
    
    private:
        //內(nèi)置類(lèi)型
    	int _year;
    	int _month;
        //自定義類(lèi)型
    	Time _day;
    };
    
    int main()
    {
    	Date d; //調(diào)用編譯器自動(dòng)生成的默認(rèn)構(gòu)造函數(shù)
    	return 0;
    }

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    我們發(fā)現(xiàn)Date的默認(rèn)構(gòu)造函數(shù)對(duì)_year和_month沒(méi)有進(jìn)行初始化,依然是隨機(jī)值,而對(duì)_day則去調(diào)用了Time類(lèi)的默認(rèn)構(gòu)造函數(shù),將其成員變量初始化為0。我們可以通過(guò)調(diào)試進(jìn)一步進(jìn)行驗(yàn)證:

    默認(rèn)構(gòu)造函數(shù)調(diào)試

  7. 值得一提的是:在C++11中,針對(duì)默認(rèn)構(gòu)造函數(shù)對(duì)內(nèi)置類(lèi)型不進(jìn)行初始化的缺陷進(jìn)行了改進(jìn),支持內(nèi)置類(lèi)型的成員變量在類(lèi)中聲明時(shí)給默認(rèn)值。如下:

    class Date
    {
    public:
    	void Print()
    	{
    		cout << _year << '-' << _month << '-' << _day << endl;
    	}
    private:
    	int _year = 0; //聲明時(shí)給默認(rèn)值
    	int _month = 0;
    	int _day = 0;
    };
    
    int main()
    {
    	Date d;
    	d.Print();
    	return 0;
    }

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

  8. ?構(gòu)造函數(shù)也支持給缺省值。無(wú)參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱(chēng)作默認(rèn)構(gòu)造函數(shù)。而默認(rèn)構(gòu)造函數(shù)只能有一個(gè),故二者不能同時(shí)存在。舉例如下【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    class Date
    {
    public:
    	Date() //無(wú)參的構(gòu)造函數(shù)
    	{
    		_year = 2023;
    		_month = 8;
    		_day = 22;
    	}
    	Date(int year = 2023, int month = 8, int day = 22) //全缺省的構(gòu)造函數(shù)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	Date d1(2024); //編譯通過(guò),調(diào)用全缺省的構(gòu)造函數(shù)
    	Date d2; //這里編譯會(huì)報(bào)錯(cuò),d2調(diào)用默認(rèn)構(gòu)造函數(shù),但存在兩個(gè)默認(rèn)構(gòu)造函數(shù),編譯器不知道調(diào)用哪個(gè)
    	return 0;
    }

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    小貼士:一般我們顯式定義構(gòu)造函數(shù)時(shí),習(xí)慣將構(gòu)造函數(shù)寫(xiě)成全缺省的,以提高代碼的健壯性。

四. 析構(gòu)函數(shù)

4.1 概念

? ? ? ? 構(gòu)造函數(shù)是在對(duì)象創(chuàng)建時(shí)對(duì)其進(jìn)行初始化,有初始化便有銷(xiāo)毀,析構(gòu)函數(shù)的作用就是在對(duì)象生命周期結(jié)束時(shí),完成對(duì)象中資源的清理和釋放。和構(gòu)造函數(shù)一樣,析構(gòu)函數(shù)由編譯器自動(dòng)調(diào)用。下面是Stack類(lèi)的構(gòu)造函數(shù)和析構(gòu)函數(shù)的實(shí)現(xiàn)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

class Stack
{
public:
	Stack(size_t capacity = 4) //構(gòu)造函數(shù),初始化一個(gè)棧,寫(xiě)成全缺省的形式
	{
		_array = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _array)
		{
			perror("malloc申請(qǐng)空間失敗!!!");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	~Stack() //析構(gòu)函數(shù),在類(lèi)名前加~號(hào)
	{
		free(_array); //堆上動(dòng)態(tài)申請(qǐng)的空間需要由用戶(hù)自行釋放
		//下面的代碼也可以不寫(xiě),棧上的空間操作系統(tǒng)會(huì)自動(dòng)釋放
		_array = nullptr;
		_capacity = _top = 0;
	}
private:
	int* _array;
	int _capacity;
	int _top;
};

4.2 特性

? ? ? ? 析構(gòu)函數(shù)也是特殊的成員函數(shù),其特征如下:

  1. 析構(gòu)函數(shù)的函數(shù)名是在類(lèi)名前加上字符 ~。
  2. 析構(gòu)函數(shù)既沒(méi)有返回值,也沒(méi)有參數(shù)
  3. 與構(gòu)造函數(shù)相反,析構(gòu)函數(shù)不能支持重載,一個(gè)類(lèi)中有且只能有一個(gè)析構(gòu)函數(shù)。參考構(gòu)造函數(shù)的特征,當(dāng)用戶(hù)沒(méi)有顯式地定義析構(gòu)函數(shù),編譯器會(huì)自動(dòng)生成一個(gè)默認(rèn)的析構(gòu)函數(shù)。
  4. 當(dāng)對(duì)象的生命周期結(jié)束時(shí),C++編譯器會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)。
    class Stack
    {
    public:
    	Stack(){
    		cout << "Stack()" << endl;
    	}
    	~Stack(){
    		cout << "~Stack()" << endl;
    	}
    private:
    	int* _array;
    	int _capacity;
    	int _top;
    };
    
    int main()
    {
    	Stack s;
    	return 0;
    }

    當(dāng)s對(duì)象創(chuàng)建時(shí)編譯器自動(dòng)調(diào)用構(gòu)造函數(shù),當(dāng)s對(duì)象生命周期結(jié)束時(shí)編譯器自動(dòng)調(diào)用析構(gòu)函數(shù),效果如下:【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

  5. 和構(gòu)造函數(shù)類(lèi)似,編譯器默認(rèn)生成的析構(gòu)函數(shù)不會(huì)對(duì)內(nèi)置類(lèi)型成員進(jìn)行清理,最終由操作系統(tǒng)自動(dòng)進(jìn)行回收即可;而對(duì)于自定義類(lèi)型成員,默認(rèn)析構(gòu)函數(shù)會(huì)去調(diào)用它的析構(gòu)函數(shù),保證其內(nèi)部每個(gè)自定義類(lèi)型成員都可以正確銷(xiāo)毀。

    ?回到我們之前的日期類(lèi)
    class Time
    {
    public:
    	Time() //Time類(lèi)的默認(rèn)構(gòu)造函數(shù)
    	{
    		cout << "Time()" << endl;
    	}
    	~Time() //Time類(lèi)的析構(gòu)函數(shù)
    	{
    		cout << "~Time()" << endl;
    	}
    private:
    	int _hours;
    	int _minute;
    	int _second;
    };
    class Date
    {
    public:
    	//沒(méi)有顯式寫(xiě)出構(gòu)造函數(shù)和析構(gòu)函數(shù),使用編譯器自動(dòng)生成的
    
    private:
    	int _year;
    	int _month;
    	Time _day;
    };
    
    int main()
    {
    	Date d; //調(diào)用編譯器自動(dòng)生成的默認(rèn)構(gòu)造函數(shù)
    	return 0;
    }

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    盡管我們沒(méi)有直接創(chuàng)建Time類(lèi)的對(duì)象,但依然調(diào)用了Time類(lèi)的構(gòu)造函數(shù)和析構(gòu)函數(shù)。這是因?yàn)?span style="background-color:#f9eda6;">Date類(lèi)中的_day成員是Time類(lèi)的對(duì)象,在Date類(lèi)的默認(rèn)構(gòu)造函數(shù)和默認(rèn)析構(gòu)函數(shù)中,會(huì)去調(diào)用Time類(lèi)這個(gè)自定義類(lèi)型的構(gòu)造函數(shù)和析構(gòu)函數(shù),對(duì)_day成員進(jìn)行初始化清理工作。

  6. 如果類(lèi)中沒(méi)有動(dòng)態(tài)申請(qǐng)內(nèi)存時(shí),析構(gòu)函數(shù)可以不寫(xiě),直接使用編譯器生成的默認(rèn)析構(gòu)函數(shù),比如Date類(lèi);有資源申請(qǐng)時(shí),一定要寫(xiě),否則會(huì)造成內(nèi)存泄漏,比如Stack類(lèi)

  7. 類(lèi)的析構(gòu)函數(shù)一般按照構(gòu)造函數(shù)調(diào)用的相反順序進(jìn)行調(diào)用,但是要注意static對(duì)象的存在, 因?yàn)閟tatic改變了對(duì)象的生存作用域,需要等待程序結(jié)束時(shí)才會(huì)析構(gòu)釋放static對(duì)象。

? ? ? ? ?Q:假設(shè)已經(jīng)有A,B,C,D 4個(gè)類(lèi)的定義,則程序中A,B,C,D析構(gòu)函數(shù)調(diào)用順序?yàn)椋?/p>

C c;
int main()
{
	A a;

	B b;

	static D d;

    return 0;

}

答案是BADC。解析如下:

1、全局變量?jī)?yōu)先于局部變量進(jìn)行構(gòu)造,因此構(gòu)造的順序?yàn)閏abd

2、析構(gòu)的順序和構(gòu)造的順序相反

3、static和全局對(duì)象需在程序結(jié)束才進(jìn)行析構(gòu),故會(huì)放在局部對(duì)象之后進(jìn)行析構(gòu)

綜上:析構(gòu)的順序即為BADC。


五. 拷貝構(gòu)造函數(shù)

5.1 概念

????????在現(xiàn)實(shí)生活中,可能存在一個(gè)與你長(zhǎng)相,我們稱(chēng)其為雙胞胎

【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

? ? ? ? ?那我們?cè)趧?chuàng)建類(lèi)對(duì)象時(shí),能不能創(chuàng)建一個(gè)和已有對(duì)象一模一樣的對(duì)象呢?Ctrl+CCtrl+V想必沒(méi)有人不喜歡吧嘿嘿【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)這就要談到我們的拷貝構(gòu)造函數(shù)惹。

? ? ? ? ?拷貝構(gòu)造函數(shù):只有單個(gè)形參,該形參是對(duì)本類(lèi)型對(duì)象的引用(一般常用const修飾),在用已存在的類(lèi)對(duì)象創(chuàng)建新對(duì)象時(shí)編譯器會(huì)自動(dòng)調(diào)用拷貝構(gòu)造函數(shù)。

class Date
{
public:
	Date() {};
	Date(const Date& d) //Date的拷貝構(gòu)造函數(shù)
	{
		_day = d._day;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1; 
	Date d2(d1); //用d1拷貝構(gòu)造d2
	return 0;
}

5.2 特性

?????????拷貝構(gòu)造函數(shù)也是屬于特殊的成員函數(shù),其特征如下:

  1. 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式
  2. 拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須是本類(lèi)對(duì)象的引用使用傳值方式編譯器會(huì)直接報(bào)錯(cuò),
    因?yàn)闀?huì)引發(fā)無(wú)窮遞歸調(diào)用
    //拷貝構(gòu)造函數(shù)的寫(xiě)法
    Date(const Date d) // 錯(cuò)誤寫(xiě)法:編譯報(bào)錯(cuò),會(huì)引發(fā)無(wú)窮遞歸
    {
    	_year = d._year;
    	_month = d._month;
    	_day = d._day;
    }
    
    Date(const Date& d) // 正確寫(xiě)法
    {
    	_year = d._year;
    	_month = d._month;
    	_day = d._day;
    }
    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)
  3. 未顯式定義,編譯器會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。 默認(rèn)的拷貝構(gòu)造函數(shù)對(duì)內(nèi)置類(lèi)型成員會(huì)按照其在內(nèi)存存儲(chǔ)的字節(jié)序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝;而對(duì)于自定義類(lèi)型成員,會(huì)去調(diào)用它的拷貝構(gòu)造函數(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:
    	// 內(nèi)置類(lèi)型
    	int _year = 2023;
    	int _month = 1;
    	int _day = 1;
    	// 自定義類(lèi)型
    	Time _t;
    };
    int main()
    {
    	Date d1; //d1調(diào)用默認(rèn)的構(gòu)造函數(shù)進(jìn)行初始化
    
    	// 用已經(jīng)存在的d1拷貝構(gòu)造d2,此時(shí)會(huì)調(diào)用Date類(lèi)的拷貝構(gòu)造函數(shù)
    	// 但Date類(lèi)并沒(méi)有顯式定義拷貝構(gòu)造函數(shù),因此編譯器會(huì)給Date類(lèi)生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)
    	Date d2(d1);
    	return 0;
    }
    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)我們可以通過(guò)監(jiān)視窗口來(lái)查看d2對(duì)象的拷貝情況【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)可以看出,編譯器默認(rèn)生成的拷貝構(gòu)造函數(shù)不僅會(huì)對(duì)自定義類(lèi)型成員進(jìn)行拷貝(通過(guò)調(diào)用相應(yīng)的拷貝構(gòu)造函數(shù)),也會(huì)對(duì)內(nèi)置類(lèi)型成員進(jìn)行拷貝(按字節(jié)序的淺拷貝)。

    默認(rèn)拷貝構(gòu)造函數(shù)調(diào)試

  4. 咦?既然編譯器默認(rèn)生成的拷貝構(gòu)造函數(shù)已經(jīng)可以很好的完成拷貝了,那我們還需要顯式實(shí)現(xiàn)拷貝構(gòu)造函數(shù)嗎【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)對(duì)于上面的日期類(lèi)確實(shí)已經(jīng)足夠了,讓我們?cè)倩氐街皩?shí)現(xiàn)的Stack類(lèi)
    class Stack
    {
    public:
    	Stack(size_t capacity = 4)
    	{
    		_array = (int*)malloc(capacity * sizeof(int));
    		if (nullptr == _array)
    		{
    			perror("malloc申請(qǐng)空間失敗");
    			return;
    		}
    		_size = 0;
    		_capacity = capacity;
    	}
    	~Stack()
    	{
    		if (_array)
    		{
    			free(_array);
    			_array = nullptr;
    			_capacity = 0;
    			_size = 0;
    		}
    	}
    private:
    	int* _array;
    	size_t _size;
    	size_t _capacity;
    };
    int main()
    {
    	Stack s1;
    	Stack s2(s1);
    	return 0;
    }

    當(dāng)我們興沖沖地運(yùn)行代碼時(shí),誒,程序居然崩潰【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    ?為什么呢?這就涉及到了淺拷貝按字節(jié)序拷貝的缺陷,如下圖所示【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    ?那這種淺拷貝的問(wèn)題要如何解決呢?

    一般有兩種解決方案:深拷貝或者引用計(jì)數(shù)。


    所謂深拷貝,就是手動(dòng)再申請(qǐng)一段空間,然后將原空間的內(nèi)容依次拷貝到新空間中,最后讓s2的_array指針指向這個(gè)新空間。這種方法避免了一塊空間被多個(gè)對(duì)象指向的問(wèn)題。

    引用計(jì)數(shù),就是在類(lèi)中額外增加一個(gè)變量count記錄堆空間被引用的次數(shù),只有當(dāng)引用次數(shù)變?yōu)?時(shí),我們才對(duì)這段空間進(jìn)行釋放。這種方法避免了一塊空間被多次釋放的問(wèn)題。


    現(xiàn)在我們對(duì)這兩種方式有個(gè)初步的印象即可,后續(xù)我們會(huì)詳細(xì)講解。不過(guò)無(wú)論是深拷貝還是引用計(jì)數(shù),都是編譯器默認(rèn)生成的拷貝構(gòu)造函數(shù)無(wú)法做到的,需要我們顯式地實(shí)現(xiàn)拷貝構(gòu)造函數(shù)。

  5. 拷貝構(gòu)造函數(shù)有三個(gè)典型的調(diào)用場(chǎng)景:使用已存在的對(duì)象創(chuàng)建新對(duì)象、函數(shù)形參為類(lèi)對(duì)象、函數(shù)返回值為類(lèi)對(duì)象。

    class Date
    {
    public:
    	Date(int year, int minute, int day)
    	{
    		cout << "Date(int,int,int):" << this << endl;
    	}
    	Date(const Date& d)
    	{
    		cout << "Date(const Date& d):" << this << endl;
    	}
    	~Date()
    	{
    		cout << "~Date():" << this << endl;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    Date Test(Date d)
    {
    	Date temp(d);
    	return temp;
    }
    int main()
    {
    	Date d1(2022, 1, 13);
    	Test(d1);
    	return 0;
    }

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

    結(jié)論:為了提高效率,減少拷貝構(gòu)造的次數(shù),一般對(duì)象傳參時(shí),我們盡量使用引用傳參,函數(shù)返回時(shí)也是根據(jù)實(shí)際場(chǎng)景,能用引用返回盡量使用引用返回


六. 運(yùn)算符重載

6.1 引入

? ? ? ? 對(duì)于內(nèi)置類(lèi)型,我們可以使用==、>號(hào)運(yùn)算符判斷它們的大小關(guān)系,可以使用+,-號(hào)運(yùn)算符對(duì)其進(jìn)行加減......如下所示【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

int main()
{
	int a = 10;
	int b = 20;
	a = a + 10;
	b = b - 10;
	cout << (a == b);
	cout << (a > b);
	//還可以使用許許多多的運(yùn)算符進(jìn)行操作,這里就不一一挪列了
	//...
	return 0;
}

? ? ? ? 但對(duì)于自定義類(lèi)型來(lái)說(shuō),也就是我們的類(lèi),這些運(yùn)算符仿佛都失效

class Date
{
public:
	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2023, 8, 24);
	Date d2(2023, 8, 25);
	d1 = d1 + 10; //d1類(lèi)對(duì)象使用+號(hào)運(yùn)算符
	d1 == d2; //d2類(lèi)對(duì)象使用==號(hào)運(yùn)算符
}

【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

? ? ? ? 很明顯編譯器報(bào)錯(cuò)了,這是因?yàn)閷?duì)于幾個(gè)固定的內(nèi)置類(lèi)型,編譯器知道它們的運(yùn)算規(guī)則,而對(duì)于我們自定義的類(lèi)型,編譯器并不知道它的運(yùn)算規(guī)則,例如d1+10究竟是年份+10還是月份+10呢?編譯器無(wú)法進(jìn)行確定,故報(bào)錯(cuò)。

? ? ? ? 有一種很簡(jiǎn)單的解決方法就是給類(lèi)定義成員函數(shù),通過(guò)調(diào)用成員函數(shù)來(lái)實(shí)現(xiàn)我們想要的運(yùn)算,如下所示:

class Date
{
public:
	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void AddYear(int val) 
	{
		_year += val;
	}
	bool isSame(const Date& d)
	{
		return _year == d._year && _month == d._month && _day == d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2022, 8, 24);
	Date d2(2023, 8, 24);
	d1.AddYear(1); //年份+1
	cout << d1.isSame(d2); //比較d1和d2是否相等
}

【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

? ? ? ? 上面的方式的確可以解決問(wèn)題,但還是不夠直觀,每次進(jìn)行運(yùn)算都需要調(diào)用函數(shù),代碼未免有點(diǎn)挫【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)有沒(méi)有什么方法可以讓類(lèi)使用運(yùn)算符進(jìn)行運(yùn)算嗎?

????????這就不得不談到我們的主角----運(yùn)算符重載

【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

6.2 概念

????????C++為了增強(qiáng)代碼的可讀性引入了運(yùn)算符重載,運(yùn)算符重載是具有特殊函數(shù)名的函數(shù),也具有其返回值類(lèi)型,函數(shù)名字以及參數(shù)列表,其返回值類(lèi)型和參數(shù)列表與普通的函數(shù)類(lèi)似。

? ? ? ? 運(yùn)算符重載的函數(shù)名為:關(guān)鍵字operator后面+需要重載的運(yùn)算符符號(hào)

? ? ? ? 其函數(shù)原型為:返回值類(lèi)型 operator操作符(參數(shù)列表)

//==號(hào)運(yùn)算符重載
bool operator==(const Date& d)
{
    //函數(shù)內(nèi)容
	return _year == d._year && _month == d._month && _day == d._day;
}

通過(guò)運(yùn)算符重載,我們可以對(duì)已有的運(yùn)算符重新進(jìn)行定義,賦予其另一種功能,以適應(yīng)不同的數(shù)據(jù)類(lèi)型,且不改變?cè)械墓δ堋?/p>

6.3 注意事項(xiàng)

? ? ? ? 進(jìn)行運(yùn)算符重載需要注意以下幾點(diǎn):

  1. 不能通過(guò)連接其他符號(hào)來(lái)創(chuàng)建新的運(yùn)算符:比如使用operator@創(chuàng)建新的運(yùn)算符@
  2. 重載操作符必須有一個(gè)類(lèi)類(lèi)型參數(shù)
  3. 不能改變內(nèi)置類(lèi)型運(yùn)算符的含義。例如:內(nèi)置的整型+,不能改變其含義
  4. 不能改變操作符的操作數(shù)個(gè)數(shù)。一個(gè)操作符有幾個(gè)操作數(shù),其重載時(shí)函數(shù)就有幾個(gè)參數(shù)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)
  5. 運(yùn)算符重載也可以作為類(lèi)成員函數(shù)。作為類(lèi)成員函數(shù)重載時(shí),其形參看起來(lái)會(huì)比操作數(shù)數(shù)目少一個(gè),因?yàn)槌蓡T函數(shù)的第一個(gè)參數(shù)為隱藏的this指針
    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)
  6. 有五個(gè)運(yùn)算符不支持重載,它們分別是:. 、::?sizeof 、?: .*
    ?

6.4 重載示例

? ? ? ? ?下面我們來(lái)看看日期類(lèi)==號(hào)運(yùn)算符的重載示例【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

? ? ? ? ?1、作為全局函數(shù)重載

class Date
{
public:
	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

//private: //為了讓==運(yùn)算符重載函數(shù)能夠訪問(wèn),將成員變量設(shè)置為共有的
	int _year;
	int _month;
	int _day;
};

//作為全局函數(shù)重載
bool operator==(const Date& d1,const Date& d2)
{
	//這里需要類(lèi)外訪問(wèn)成員變量
	return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day; 
}
int main()
{
	Date d1(2022, 8, 24);
	Date d2(2023, 8, 24);
	cout << (d1 == d2);
	return 0;
}

當(dāng)運(yùn)算符重載作為全局函數(shù)時(shí),由于我們難免需要對(duì)成員變量進(jìn)行訪問(wèn),我們需要類(lèi)的成員函數(shù)是共有的,可這難免會(huì)破壞了類(lèi)的封裝性。


當(dāng)然,我們還可以使用友元函數(shù)來(lái)解決,關(guān)于友元在下篇會(huì)介紹到。不過(guò)最推薦的還是將其重載為成員函數(shù)

? ? ? ? ?2、作為成員函數(shù)重載

class Date
{
public:
	Date(int year = 2000, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// bool operator==(Date* const this, const Date& d2)
	// this指向調(diào)用的對(duì)象
	bool operator==(const Date& d) //重載為成員函數(shù)
	{
		//類(lèi)內(nèi)訪問(wèn)成員變量不受訪問(wèn)限定符限制
		return _year == d._year && _month == d._month && _day == d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022, 8, 24);
	Date d2(2023, 8, 24);
	//下面兩種寫(xiě)法是等價(jià)的
	cout << (d1 == d2);
	cout << d1.operator==(d2);
	return 0;
}

6.5 賦值運(yùn)算符重載

? ? ? ? 在使用類(lèi)的過(guò)程中,當(dāng)我們想將一個(gè)類(lèi)賦值給另一個(gè)類(lèi)時(shí),我們便可以對(duì)=賦值運(yùn)算符進(jìn)行重載。其重載格式如下:

  • 參數(shù)類(lèi)型:const T&,傳遞引用可以提高傳參效率,const保證原來(lái)的類(lèi)對(duì)象不會(huì)被修改
  • 返回值類(lèi)型:T&,使用引用返回可以提高返回的效率,有返回值目的是為了支持鏈?zhǔn)皆L問(wèn)
  • 一般會(huì)檢測(cè)是否是自己給自己賦值,如果是,則不進(jìn)行任何操作
  • 最終返回的是本身的引用,即*this,便于進(jìn)行連續(xù)賦值
    class Date
    {
    public:
    	Date(int year = 2023, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	Date& operator=(const Date& d) //賦值運(yùn)算符重載
    	{
    		if (this != &d) //避免自己給自己賦值
    		{
    			_year = d._year;
    			_month = d._month;
    			_day = d._day;
    		}
    		return *this; //返回自身的引用,連續(xù)賦值
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	Date d1;
    	Date d2, d3;
    	d3 = d2 = d1; //調(diào)用賦值運(yùn)算符重載
    }

?????????下面是賦值運(yùn)算符重載的幾點(diǎn)注意事項(xiàng)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

  1. 賦值運(yùn)算符只能重載為成員函數(shù)不能重載成全局函數(shù)
    // 賦值運(yùn)算符重載成全局函數(shù)
    Date& operator=(Date& left, const Date& right)
    {
    	if (&left != &right)
    	{
    		left._year = right._year;
    		left._month = right._month;
    		left._day = right._day;
    	}
    	return left;
    }
    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)為什么呢?實(shí)際上,在我們前面介紹的默認(rèn)成員函數(shù)中,賦值運(yùn)算符重載也在其中。因此,當(dāng)我們?cè)陬?lèi)中沒(méi)有顯式地實(shí)現(xiàn),編譯器會(huì)自動(dòng)生成默認(rèn)的重載函數(shù)。而如果用戶(hù)又自行在類(lèi)外寫(xiě)一個(gè)賦值運(yùn)算符重載函數(shù),就會(huì)和編譯器默認(rèn)生成的默認(rèn)賦值運(yùn)算符重載沖突,故賦值運(yùn)算符只能重載為成員函數(shù)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)
  2. 編譯器默認(rèn)生成的賦值運(yùn)算符重載函數(shù),其規(guī)則和拷貝構(gòu)造函數(shù)相似。對(duì)于內(nèi)置類(lèi)型,是按照字節(jié)序進(jìn)行拷貝的;而對(duì)于自定義類(lèi)型,則是去調(diào)用它的賦值運(yùn)算符重載函數(shù)
  3. 那么既然編譯器默認(rèn)生成的賦值運(yùn)算符重載已經(jīng)可以完成字節(jié)序的拷貝了,那還要顯式地進(jìn)行實(shí)現(xiàn)嗎?答案顯然是需要的。和拷貝構(gòu)造函數(shù)一樣,在實(shí)現(xiàn)Stack這種有內(nèi)存管理的類(lèi),我們需要自行實(shí)現(xiàn)賦值運(yùn)算符重載,否則就會(huì)因?yàn)?span style="color:#fe2c24;">淺拷貝的問(wèn)題導(dǎo)致程序崩潰
  4. 反之,如果程序不涉及內(nèi)存管理,賦值運(yùn)算符重載是否顯式實(shí)現(xiàn)都可以

? ? ? ? 下面來(lái)個(gè)小問(wèn)題試試對(duì)拷貝構(gòu)造的理解:

int main()
{
	Date d1, d2;
	Date d3 = d1; //這里調(diào)用的是拷貝構(gòu)造還是賦值重載呢?
	d2 = d1; //這里呢?
	return 0;
}

答:第一問(wèn)調(diào)用的是拷貝構(gòu)造函數(shù),第二問(wèn)調(diào)用的是賦值重載。

解析:拷貝構(gòu)造函數(shù)用已存在的對(duì)象去構(gòu)造新對(duì)象,而d3就是我們需要構(gòu)造的新對(duì)象,第一問(wèn)就是用d1對(duì)象去構(gòu)造d3對(duì)象,故調(diào)用拷貝構(gòu)造,這種寫(xiě)法與Date d3(d1)等價(jià)。而賦值運(yùn)算符載函數(shù)兩個(gè)已存在對(duì)象之間進(jìn)行賦值,d1和d2都是已經(jīng)存在的對(duì)象,故d2=d1調(diào)用的是賦值重載。

6.6 前置++和后置++運(yùn)算符重載

? ? ? ? 前置++

? ? ? ? 下面我們來(lái)嘗試對(duì)Date類(lèi)實(shí)現(xiàn)前置++運(yùn)算符的重載,用于對(duì)天數(shù)進(jìn)行自增。所謂前置++,就是先自增1再返回結(jié)果,如下:

class Date
{
public:
	Date(int year = 2023, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator++() //前置++運(yùn)算符重載
	{
		_day += 1; //先自增  (注意:這里為了演示先忽略日期進(jìn)位,進(jìn)位處理請(qǐng)看Date類(lèi)的模擬實(shí)現(xiàn))
		return *this; //返回自身,為了提高效率,用引用返回
	}
private:
	int _year;
	int _month;
	int _day;
};

? ? ? ? 后置++

? ? ? ? 而后置++,就是先返回結(jié)果再進(jìn)行自增。但有個(gè)問(wèn)題:前置++和后置++都是一元運(yùn)算符,即只有一個(gè)操作數(shù):對(duì)象本身。在作為成員函數(shù)重載時(shí),重載函數(shù)就是無(wú)參的,那編譯器要如何區(qū)分是前置++還是后置++呢?

int main()
{
	Date d;
	//編譯器要如何區(qū)分哪個(gè)operator++()函數(shù)是前置++,哪個(gè)又是后置++ ???
	++d; //相當(dāng)于d.operator++()
	d++; //也相當(dāng)于d.operator++()
	return 0;
}

? ? ? ? 為此,C++做了特殊規(guī)定:后置++重載時(shí)多增加一個(gè)int類(lèi)型的參數(shù)用于占位,但調(diào)用函數(shù)時(shí)該參數(shù)不用傳遞,編譯器會(huì)自動(dòng)傳遞。

class Date
{
public:
	Date(int year = 2023, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date operator++(int) //后置++運(yùn)算符重載,int用于占位區(qū)分
	{
		Date temp(*this); //由于要返回+1前的結(jié)果,所以先對(duì)對(duì)象進(jìn)行拷貝
		_day += 1; //然后天數(shù)+1
		return temp; //然后將+1前的對(duì)象返回。由于temp出了函數(shù)就銷(xiāo)毀了,故不能用引用返回
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d;
	++d; //相當(dāng)于d.operator++()
	d++; //相當(dāng)于d.operator++(int),int類(lèi)型參數(shù)由編譯器自動(dòng)傳遞
	return 0;
}

七. const成員函數(shù)

7.1 問(wèn)題引入

? ? ? ? 我們來(lái)看下面的代碼:

class Date
{
public:
	void Print()
	{
		cout << "void Print()" << endl;
	}
private:
	int _year = 2023;
	int _month = 1;
	int _day = 1;
};

int main()
{
	const Date d;
	d.Print();
	return 0;
}

? ? ? ? 上面的代碼編譯時(shí)會(huì)進(jìn)行報(bào)錯(cuò),報(bào)錯(cuò)原因如下【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

由于對(duì)象d被const所修飾,故其類(lèi)型為const Date,表示不能對(duì)類(lèi)的任何成員進(jìn)行修改。當(dāng)d調(diào)用Print函數(shù)時(shí),傳入的實(shí)參就是d的地址,即const Date*類(lèi)型的指針,而在Print函數(shù)中,用于接收的this指針卻是Date*類(lèi)型的,這無(wú)疑是一種權(quán)限的放大,故編譯器會(huì)進(jìn)行報(bào)錯(cuò)。


那要怎么解決這個(gè)問(wèn)題呢?很簡(jiǎn)單,給this指針加上const進(jìn)行修飾即可。


7.2 定義方式

? ? ? ? 由于this指針是 "非靜態(tài)成員函數(shù)" 的隱藏形參,我們無(wú)法顯式地去定義this指針,因此C++規(guī)定,在成員函數(shù)后面加上const代表它為const成員函數(shù),其this指針的類(lèi)型為const A* this,編譯器會(huì)自動(dòng)進(jìn)行識(shí)別處理【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)


? ? ? ? ?回到上面的代碼,當(dāng)我們?cè)赑rint函數(shù)后加上const后,程序就正常運(yùn)行啦【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

class Date
{
public:
	void Print() const //this指針的類(lèi)型是const Date*
	{
		cout << "void Print() const" << endl;
	}
private:
	int _year = 2023;
	int _month = 1;
	int _day = 1;
};

int main()
{
	const Date d;
	d.Print();
	return 0;
}

【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)


7.3 使用細(xì)則

  1. const修飾的成員函數(shù)和非const修飾的成員函數(shù)可以構(gòu)成函數(shù)重載,調(diào)用時(shí)會(huì)調(diào)用最匹配的函數(shù),一般用作讀寫(xiě)的分離。
    class Date
    {
    public: 
    	void Print()  //非const成員函數(shù)
    	{
    		cout << "void Print()" << endl;
    	}
    	void Print() const  //const成員函數(shù)
    	{
    		cout << "void Print() const" << endl;
    	}
    private:
    	int _year = 2023;
    	int _month = 1;
    	int _day = 1;
    };
    
    int main()
    {
    	const Date d1;
    	Date d2;
    	d1.Print(); //const類(lèi)型的對(duì)象調(diào)用cosnt成員函數(shù)
    	d2.Print(); //非const類(lèi)型的對(duì)象調(diào)用非const成員函數(shù)
    	return 0;
    }

    【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載),C++深入淺出,c++,開(kāi)發(fā)語(yǔ)言,構(gòu)造函數(shù),析構(gòu)函數(shù),運(yùn)算符重載,默認(rèn)成員函數(shù)

  2. 建議給只讀成員函數(shù)加上const修飾,即內(nèi)部不涉及修改成員變量的函數(shù)?

  3. 構(gòu)造函數(shù)不能加const修飾。構(gòu)造函數(shù)是對(duì)成員變量進(jìn)行初始化的,顯然會(huì)涉及到成員變量的修改。


?八. 取地址運(yùn)算符重載

? ? ? ? 取地址運(yùn)算符重載有兩個(gè)版本,一個(gè)是const的,一個(gè)是非const的。這兩個(gè)成員函數(shù)也是我們一開(kāi)始講的默認(rèn)成員函數(shù),當(dāng)用戶(hù)沒(méi)有顯式定義時(shí),編譯器會(huì)自動(dòng)生成。

class Date
{
public:
	Date* operator&() //非const版本,this指針類(lèi)型為Date*
	{
		return this;
	}
	const Date* operator&()const //const版本,this指針類(lèi)型為const Date*
	{
		return this;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

這兩個(gè)版本的成員函數(shù)和上面的不同,一般使用編譯器默認(rèn)生成的取地址重載即可。只有特殊情況下,才需要顯式定義,比如想讓別人獲取到指定的內(nèi)容!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-707602.html

到了這里,關(guān)于【C++深入淺出】類(lèi)和對(duì)象中篇(六種默認(rèn)成員函數(shù)、運(yùn)算符重載)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【C++深入淺出】C/C++內(nèi)存管理(教你如何new到對(duì)象)

    【C++深入淺出】C/C++內(nèi)存管理(教你如何new到對(duì)象)

    ? ? ? ? 前面我們學(xué)習(xí)了有關(guān)C++類(lèi)和對(duì)象的知識(shí),學(xué)會(huì)了如何構(gòu)建一個(gè)完整的類(lèi),這些類(lèi)都是存儲(chǔ)在 ??臻g 上的。在C語(yǔ)言中,我們不僅可以在棧上定義變量,也可以對(duì) 堆 上的空間進(jìn)行管理,在接下來(lái)的幾期中,我們的目標(biāo)就是 學(xué)會(huì)C++中是如何進(jìn)行內(nèi)存管理的 。 ? ? ? ?

    2024年02月08日
    瀏覽(31)
  • 【深入淺出Spring Security(三)】默認(rèn)登錄認(rèn)證的實(shí)現(xiàn)原理

    【深入淺出Spring Security(三)】默認(rèn)登錄認(rèn)證的實(shí)現(xiàn)原理

    由默認(rèn)的 SecurityFilterChain 為例(即表單登錄),向服務(wù)器請(qǐng)求 /hello 資源Spring Security 的流程分析如下: 請(qǐng)求 /hello 接口,在引入 Spring Security 之后會(huì)先經(jīng)過(guò)一系列過(guò)濾器(一中請(qǐng)求的是 /test 接口); 在請(qǐng)求到達(dá) FilterSecurityInterceptor 時(shí),發(fā)現(xiàn)請(qǐng)求并未認(rèn)證。請(qǐng)求被攔截下來(lái),并

    2024年02月09日
    瀏覽(29)
  • 【C++】類(lèi)和對(duì)象(中篇)

    【C++】類(lèi)和對(duì)象(中篇)

    在往期 類(lèi)和對(duì)象(上篇) 中,我們初步接觸了C++中的類(lèi)和對(duì)象,接下來(lái)我會(huì)和大家分享有關(guān)這個(gè)知識(shí)點(diǎn)更多的內(nèi)容~ 如果一個(gè)類(lèi)中什么成員都沒(méi)有,簡(jiǎn)稱(chēng)為空類(lèi)??疹?lèi)中真的什么都沒(méi)有嗎?并不是,任何類(lèi)在什么都不寫(xiě)時(shí),編譯器會(huì)自動(dòng)生成默認(rèn)成員函數(shù)。 默認(rèn)成員函數(shù):用戶(hù)

    2024年02月15日
    瀏覽(17)
  • C++好難(3):類(lèi)和對(duì)象(中篇)

    C++好難(3):類(lèi)和對(duì)象(中篇)

    類(lèi)的6個(gè)默認(rèn)成員函數(shù) 構(gòu)造函數(shù) 析構(gòu)函數(shù) 拷貝構(gòu)造函數(shù) 賦值運(yùn)算符重載 const成員函數(shù) 取地址及const取地址操作符重載 目錄 【本章目標(biāo)】 1.類(lèi)的6個(gè)默認(rèn)成員函數(shù) 2.構(gòu)造函數(shù) 2.1概念 2.2構(gòu)造函數(shù)的特性 特性一 特性二 特性三 特性四 特性五 特性六 特性七 2.3總結(jié) 3.析構(gòu)函數(shù) 3.

    2024年02月03日
    瀏覽(22)
  • C++從入門(mén)到精通——類(lèi)和對(duì)象(中篇)

    C++從入門(mén)到精通——類(lèi)和對(duì)象(中篇)

    如果一個(gè)類(lèi)中什么成員都沒(méi)有,簡(jiǎn)稱(chēng)為空類(lèi)??疹?lèi)中什么都沒(méi)有嗎?并不是的,任何一個(gè)類(lèi)在我們不寫(xiě)的情況下,都會(huì)自動(dòng)生成下面6個(gè)默認(rèn)成員函數(shù)。 對(duì)于以下的日期類(lèi): 運(yùn)行結(jié)果: 對(duì)于Date類(lèi),可以通過(guò)SetDate公有的方法給對(duì)象設(shè)置內(nèi)容,但是如果每次創(chuàng)建對(duì)象都調(diào)用該方

    2024年04月12日
    瀏覽(28)
  • 【深入淺出C#】章節(jié) 4: 面向?qū)ο缶幊袒A(chǔ):封裝、繼承和多態(tài)

    封裝、繼承和多態(tài)是面向?qū)ο缶幊讨械暮诵母拍?,它們?duì)于構(gòu)建靈活、可擴(kuò)展和可維護(hù)的軟件系統(tǒng)至關(guān)重要。 封裝(Encapsulation)通過(guò)將數(shù)據(jù)和相關(guān)操作封裝在一個(gè)類(lèi)中,隱藏內(nèi)部實(shí)現(xiàn)細(xì)節(jié),并提供公共接口來(lái)與外部進(jìn)行交互。封裝有助于保護(hù)數(shù)據(jù)的完整性和安全性,同時(shí)提

    2024年02月10日
    瀏覽(27)
  • 【C++深入淺出】模版初識(shí)

    【C++深入淺出】模版初識(shí)

    目錄 一. 前言 二. 泛型編程 三. 函數(shù)模版? 3.1 函數(shù)模版的概念 3.2 函數(shù)模版的格式 3.3 函數(shù)模版的原理 3.4 函數(shù)模板的實(shí)例化 3.5 模板參數(shù)的匹配原則 四. 類(lèi)模版 4.1 類(lèi)模版的定義 4.2 類(lèi)模版的實(shí)例化 ? ? ? ? 本期我們要介紹的是C++的又一大重要功能---- 模版 。通過(guò)模版,我們

    2024年02月08日
    瀏覽(105)
  • 深入淺出C++ ——線程庫(kù)

    深入淺出C++ ——線程庫(kù)

    ??在C++11之前,涉及到多線程問(wèn)題,都是和平臺(tái)相關(guān)的,比如windows和linux下各有自己的接口,這使得代碼的可移植性比較差。C++11中最重要的特性就是對(duì)線程進(jìn)行支持了,使得C++在并行編程時(shí)不需要依賴(lài)第三方庫(kù),而且在原子操作中還引入了 原子類(lèi) 的概念。要使用標(biāo)準(zhǔn)庫(kù)中

    2024年02月03日
    瀏覽(98)
  • 深入淺出C++——C++的類(lèi)型轉(zhuǎn)換

    在C語(yǔ)言中,如果 賦值運(yùn)算符左右兩側(cè)類(lèi)型不同 ,或者形參與實(shí)參類(lèi)型不匹配,或者 返回值類(lèi)型與接收返回值類(lèi)型不一致 時(shí),就需要發(fā)生類(lèi)型轉(zhuǎn)化。 C語(yǔ)言中總共有兩種形式的類(lèi)型轉(zhuǎn)換: 隱式類(lèi)型轉(zhuǎn)換:編譯器在編譯階段自動(dòng)進(jìn)行轉(zhuǎn)換,不能轉(zhuǎn)就編譯失敗。 顯式類(lèi)型轉(zhuǎn)換:

    2024年02月07日
    瀏覽(94)
  • 深入淺出C++ ——C++11

    深入淺出C++ ——C++11

    ????1998年是C++標(biāo)準(zhǔn)委員會(huì)成立的第一年,本來(lái)計(jì)劃以后每5年視實(shí)際需要更新一次標(biāo)準(zhǔn),C++國(guó)際標(biāo)準(zhǔn)委員會(huì)在研究C++ 03的下一個(gè)版本的時(shí)候,一開(kāi)始計(jì)劃是2007年發(fā)布,所以最初這個(gè)標(biāo)準(zhǔn)叫C++07。但是到06年的時(shí)候,官方覺(jué)得2007年肯定完不成C++ 07,而且官方覺(jué)得2008年可能也

    2024年02月02日
    瀏覽(35)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包