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

【C++練級(jí)之路】【Lv.3】類(lèi)和對(duì)象(中)(沒(méi)掌握類(lèi)的6個(gè)默認(rèn)成員函數(shù),那你根本就沒(méi)學(xué)過(guò)C++?。?/h1>

這篇具有很好參考價(jià)值的文章主要介紹了【C++練級(jí)之路】【Lv.3】類(lèi)和對(duì)象(中)(沒(méi)掌握類(lèi)的6個(gè)默認(rèn)成員函數(shù),那你根本就沒(méi)學(xué)過(guò)C++?。OM麑?duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。


歡迎各位小伙伴關(guān)注我的專(zhuān)欄,和我一起系統(tǒng)學(xué)習(xí)C++,共同探討和進(jìn)步哦!

學(xué)習(xí)專(zhuān)欄

《進(jìn)擊的C++》

引言

在C++的學(xué)習(xí)中,類(lèi)和對(duì)象章節(jié)的學(xué)習(xí)尤為重要,猶如堅(jiān)固的地基,基礎(chǔ)不牢,地動(dòng)山搖;而默認(rèn)成員函數(shù)的學(xué)習(xí),在類(lèi)和對(duì)象的學(xué)習(xí)里最為重要。所以要學(xué)好C++,學(xué)好默認(rèn)成員函數(shù)是一道必經(jīng)之路,這樣后續(xù)才能很好的學(xué)習(xí)后續(xù)模板,繼承,多態(tài)等知識(shí)。

一、類(lèi)的6個(gè)默認(rèn)成員函數(shù)

如果一個(gè)類(lèi)中什么成員都沒(méi)有,簡(jiǎn)稱(chēng)為空類(lèi)。

空類(lèi)中真的什么都沒(méi)有嗎?并不是,任何類(lèi)在什么都不寫(xiě)時(shí),編譯器會(huì)自動(dòng)生成以下6個(gè)默認(rèn)成員函數(shù)

默認(rèn)成員函數(shù):用戶(hù)沒(méi)有顯式實(shí)現(xiàn),編譯器會(huì)生成的成員函數(shù)稱(chēng)為默認(rèn)成員函數(shù)。

【C++練級(jí)之路】【Lv.3】類(lèi)和對(duì)象(中)(沒(méi)掌握類(lèi)的6個(gè)默認(rèn)成員函數(shù),那你根本就沒(méi)學(xué)過(guò)C++?。?進(jìn)擊的C++,c++,開(kāi)發(fā)語(yǔ)言,java

二、構(gòu)造函數(shù)(constructor)

2.1 引入

先來(lái)簡(jiǎn)易實(shí)現(xiàn)一個(gè)日期類(lèi)Date

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(2023, 12, 5);
	d1.Print();
	return 0;
}

我們發(fā)現(xiàn),如果每次都要自己調(diào)用初始化函數(shù),未免有點(diǎn)麻煩,并且容易忘記從而導(dǎo)致出錯(cuò)。那能否在對(duì)象創(chuàng)建時(shí),就將信息設(shè)置進(jìn)去呢?

那么,構(gòu)造函數(shù)就可以完美解決這個(gè)問(wèn)題。

2.2 概念

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

2.3 特性

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

構(gòu)造函數(shù)特性如下:

  1. 函數(shù)名與類(lèi)名相同。
  2. 無(wú)返回值。
  3. 對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
  4. 構(gòu)造函數(shù)可以重載。
class Date
{
public:
	// 1.無(wú)參構(gòu)造函數(shù)
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
	// 2.帶參構(gòu)造函數(shù)
	Date(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;//調(diào)用無(wú)參構(gòu)造函數(shù)
	d1.Print();
	Date d2(2023, 12, 5);//調(diào)用帶參構(gòu)造函數(shù)
	d2.Print();
	//warning
	Date d3();//函數(shù)聲明
	return 0;
}

注意:

  • 無(wú)返回值,不是寫(xiě)void類(lèi)型,而是什么都不寫(xiě)
  • 如果通過(guò)無(wú)參構(gòu)造函數(shù)創(chuàng)建對(duì)象時(shí),對(duì)象后面不用跟括號(hào),否則就成了函數(shù)聲明

上述簡(jiǎn)化寫(xiě)法:

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)//使用缺省參數(shù)
	{
		_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.Print();
	Date d2(2023, 12, 5);
	d2.Print();
	return 0;
}
  1. 如果類(lèi)中沒(méi)有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù),一旦用戶(hù)顯式定義編譯器將不再生成。

有人可能會(huì)疑惑,既然編譯器會(huì)自動(dòng)生成默認(rèn)構(gòu)造函數(shù),那是不是就不用自己顯式定義了呢?其實(shí),并不然。

  • 因?yàn)榫幾g器自動(dòng)生成的構(gòu)造函數(shù),只會(huì)對(duì)自定義類(lèi)型成員調(diào)用其構(gòu)造函數(shù),而不會(huì)對(duì)內(nèi)置類(lèi)型成員(char、int……)處理。
  • 所以,我們要盡量顯式定義,而不去用自動(dòng)生成的構(gòu)造函數(shù)。
  • 當(dāng)然,C++11 中針對(duì)內(nèi)置類(lèi)型成員不初始化的缺陷,又打了補(bǔ)丁,即:內(nèi)置類(lèi)型成員變量在類(lèi)中聲明時(shí)可以給默認(rèn)值。
class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year = 1;//聲明時(shí)給默認(rèn)值
	int _month = 1;
	int _day = 1;
};

int main()
{
	Date d1;//調(diào)用默認(rèn)構(gòu)造函數(shù)
	d1.Print();
	return 0;
}
  1. 一個(gè)類(lèi)中只能存在一個(gè)默認(rèn)構(gòu)造函數(shù)。比如:無(wú)參構(gòu)造函數(shù),全缺省構(gòu)造函數(shù),編譯器自動(dòng)生成的默認(rèn)構(gòu)造函數(shù)。

簡(jiǎn)單理解:不用傳參,就是調(diào)用默認(rèn)構(gòu)造函數(shù)。

舉個(gè)例子,請(qǐng)看下面代碼:

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

int main()
{
	Date d1;//報(bào)錯(cuò),對(duì)重載函數(shù)的調(diào)用不明確
	d1.Print();
	return 0;
}

分析:上述代碼包含兩個(gè)默認(rèn)構(gòu)造函數(shù),在無(wú)參調(diào)用時(shí),就會(huì)產(chǎn)生歧義。所以,才會(huì)規(guī)定一個(gè)類(lèi)只能出現(xiàn)一個(gè)默認(rèn)構(gòu)造函數(shù)。

三、析構(gòu)函數(shù)(destructor)

3.1 概念

通過(guò)前面構(gòu)造函數(shù)的學(xué)習(xí),我們知道一個(gè)對(duì)象是怎么自動(dòng)初始化的,那一個(gè)對(duì)象的內(nèi)容又是如何自動(dòng)銷(xiāo)毀的呢?

析構(gòu)函數(shù)(destructor):與構(gòu)造函數(shù)功能相反,析構(gòu)函數(shù)不是完成對(duì)對(duì)象本身的銷(xiāo)毀,局部對(duì)象銷(xiāo)毀工作是由編譯器完成的。而對(duì)象在銷(xiāo)毀時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),完成對(duì)象中資源的清理工作。

3.2 特性

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

  1. 析構(gòu)函數(shù)名是在類(lèi)名前加上字符 ~。
  2. 無(wú)參數(shù)無(wú)返回值。
  3. 對(duì)象生命周期結(jié)束時(shí),C++編譯系統(tǒng)系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù)。
  4. 析構(gòu)函數(shù)不能重載。
  5. 一個(gè)類(lèi)只能有一個(gè)析構(gòu)函數(shù)。

簡(jiǎn)易實(shí)現(xiàn)一個(gè)棧類(lèi)Stack

class Stack
{
public:
	Stack(int capacity = 4)//構(gòu)造函數(shù)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_top = 0;
		_capacity = capacity;
	}
	void Push(int x)
	{
		//CheckCapacity();
		_a[_top++] = x;
	}
	//...
	~Stack()//析構(gòu)函數(shù)
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st;
	st.Push(1);
	st.Push(2);
	return 0;
}
  1. 同樣,與構(gòu)造函數(shù)相同,若未顯式定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。只會(huì)對(duì)自定義類(lèi)型成員調(diào)用其析構(gòu)函數(shù),而不會(huì)對(duì)內(nèi)置類(lèi)型成員(char、int……)處理。

回顧往期題目232.用棧實(shí)現(xiàn)隊(duì)列(LeetCode),當(dāng)時(shí)我們用C語(yǔ)言實(shí)現(xiàn)的隊(duì)列,對(duì)比一下如今用C++實(shí)現(xiàn)。

C:

typedef struct
{
    ST pushst;
    ST popst;
} MyQueue;
 
MyQueue* myQueueCreate()
{
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    if (obj == NULL)
    {
        perror("malloc fail");
        return NULL;
    }
 
    STInit(&obj->pushst);
    STInit(&obj->popst);
 
    return obj;
}

C++:

class MyQueue
{
public:
	void Push()
	{}
	//...
private:
	Stack _pushst;
	Stack _popst;
};

分析:

  • C++實(shí)現(xiàn),在MyQueue類(lèi)中,默認(rèn)生成構(gòu)造函數(shù)(MyQueue)和析構(gòu)函數(shù)(~MyQueue)。
  • 對(duì)于Stack類(lèi)的自定義類(lèi)型成員,自動(dòng)調(diào)用對(duì)應(yīng)的默認(rèn)構(gòu)造函數(shù)(Stack)和析構(gòu)函數(shù)(~Stack),大大簡(jiǎn)化代碼。
  1. 如果類(lèi)中沒(méi)有申請(qǐng)資源時(shí),析構(gòu)函數(shù)可以不寫(xiě),直接使用編譯器生成的默認(rèn)析構(gòu)函數(shù),比如Date類(lèi);有資源申請(qǐng)時(shí),一定要寫(xiě),否則會(huì)造成資源泄漏,比如Stack類(lèi)。

四、拷貝構(gòu)造函數(shù)(copy constructor)

4.1 概念

在創(chuàng)建對(duì)象時(shí),可否創(chuàng)建一個(gè)與已存在對(duì)象一模一樣的新對(duì)象呢?

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

4.2 特性

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

  1. 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式。
  2. 拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須是類(lèi)類(lèi)型對(duì)象的引用,使用傳值方式編譯器直接報(bào)錯(cuò),因?yàn)闀?huì)引發(fā)無(wú)窮遞歸調(diào)用。
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)//拷貝構(gòu)造函數(shù)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Print();
	Date d2(2023, 12, 5);
	d2.Print();
	Date d3(d2);//拷貝構(gòu)造
	d3.Print();
	return 0;
}

大家可能會(huì)很疑惑,為什么只能是傳引用,而不能是傳值呢?

請(qǐng)看下面代碼:

//傳值傳參
void Func1(Date d)
{}
//傳引用傳參
void Func2(Date& d)
{}

int main()
{
	Date d;
	Func1(d);//傳值調(diào)用
	Func2(d);//傳引用調(diào)用
	return 0;
}

經(jīng)過(guò)調(diào)試,我們可以發(fā)現(xiàn):在傳值傳參時(shí),會(huì)調(diào)用拷貝構(gòu)造函數(shù);而在傳引用傳參時(shí),并不需要。

那么,再看看寫(xiě)成傳值傳參的拷貝構(gòu)造函數(shù),是多么的恐怖。

Date(Date d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

分析:

  • 每次要拷貝對(duì)象時(shí),傳值傳參
  • 而傳值傳參,又要拷貝對(duì)象
  • 這樣,就會(huì)形成無(wú)窮遞歸。這其實(shí)類(lèi)似于先有雞,還是先有蛋的悖論。

【C++練級(jí)之路】【Lv.3】類(lèi)和對(duì)象(中)(沒(méi)掌握類(lèi)的6個(gè)默認(rèn)成員函數(shù),那你根本就沒(méi)學(xué)過(guò)C++?。?進(jìn)擊的C++,c++,開(kāi)發(fā)語(yǔ)言,java

  1. 若未顯式定義,編譯器會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。 在編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)中,內(nèi)置類(lèi)型成員是按照字節(jié)方式直接拷貝的,這種拷貝叫做淺拷貝,或者值拷貝。而自定義類(lèi)型成員是調(diào)用其拷貝構(gòu)造函數(shù)完成拷貝的。

那么,如果每次都直接使用默認(rèn)生成的拷貝構(gòu)造函數(shù)可以嗎?答案是,當(dāng)然不可以!

再來(lái)回顧一下棧類(lèi)Stack,試試默認(rèn)生成的拷貝構(gòu)造函數(shù)。

class Stack
{
public:
	Stack(int capacity = 10)//構(gòu)造函數(shù)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			return;
		}
		_top = 0;
		_capacity = capacity;
	}
	void Push(int x)
	{
		//CheckCapacity();
		_a[_top++] = x;
	}
	//...
	~Stack()//析構(gòu)函數(shù)
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}

運(yùn)行的時(shí)候會(huì)發(fā)現(xiàn),上述程序會(huì)崩潰,這是為什么呢?

分析:

  • 此時(shí)把s1拷貝給s2,因?yàn)闆](méi)有顯式定義拷貝構(gòu)造函數(shù),所以默認(rèn)生成拷貝構(gòu)造函數(shù)。
  • 而默認(rèn)生成的拷貝構(gòu)造函數(shù),對(duì)于內(nèi)置類(lèi)型都是淺拷貝,即把值復(fù)制過(guò)去。
  • 所以,就造成了s2的_a與s1的_a指向同一塊空間
  • 那么在調(diào)用析構(gòu)函數(shù)資源銷(xiāo)毀時(shí),就會(huì)重復(fù)釋放同一塊動(dòng)態(tài)開(kāi)辟的空間,導(dǎo)致程序崩潰

【C++練級(jí)之路】【Lv.3】類(lèi)和對(duì)象(中)(沒(méi)掌握類(lèi)的6個(gè)默認(rèn)成員函數(shù),那你根本就沒(méi)學(xué)過(guò)C++!),進(jìn)擊的C++,c++,開(kāi)發(fā)語(yǔ)言,java
那么,這種情況就不是淺拷貝能解決的了,則必須顯式定義拷貝構(gòu)造函數(shù)進(jìn)行深拷貝


  1. 類(lèi)中如果沒(méi)有涉及資源申請(qǐng)時(shí),拷貝構(gòu)造函數(shù)是否寫(xiě)都可以;一旦涉及到資源申請(qǐng)時(shí),則拷貝構(gòu)造函數(shù)是一定要寫(xiě)的,否則就是淺拷貝。

深拷貝實(shí)現(xiàn)如下:

typedef int DataType;
class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (DataType*)malloc(capacity * sizeof(DataType));
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	Stack(const Stack& st)//深拷貝
	{
		_a = (DataType*)malloc(st._capacity * sizeof(DataType));
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = st._top;
		_capacity = st._capacity;
	}
	~Stack()
	{
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	DataType* _a;
	int _top;
	int _capacity;
};

五、構(gòu)造、析構(gòu)、拷貝構(gòu)造函數(shù)總結(jié)對(duì)比

5.1 構(gòu)造函數(shù)

  1. 函數(shù)名:類(lèi)名
  2. 無(wú)返回值
  3. 自動(dòng)調(diào)用
  4. 可重載
  5. 默認(rèn)生成的構(gòu)造函數(shù),只對(duì)自定義類(lèi)型成員調(diào)用其對(duì)應(yīng)的構(gòu)造函數(shù)
  6. 一個(gè)類(lèi)只能有一個(gè)默認(rèn)構(gòu)造函數(shù)
  7. 根據(jù)需求,自行判斷是否需要顯式定義

5.2 析構(gòu)函數(shù)

  1. 函數(shù)名:~類(lèi)名
  2. 無(wú)參無(wú)返回值
  3. 自動(dòng)調(diào)用
  4. 不可重載
  5. 默認(rèn)生成的析構(gòu)函數(shù),只對(duì)自定義類(lèi)型成員調(diào)用其對(duì)應(yīng)的析構(gòu)函數(shù)
  6. 一個(gè)類(lèi)只能有一個(gè)析構(gòu)函數(shù)
  7. 有資源申請(qǐng),一定要顯式定義

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

  1. 構(gòu)造函數(shù)的重載(擁有構(gòu)造函數(shù)特性1,2,3)
  2. 參數(shù):類(lèi)對(duì)象的引用
  3. 默認(rèn)生成的拷貝構(gòu)造函數(shù),只對(duì)自定義類(lèi)型成員調(diào)用其對(duì)應(yīng)的拷貝構(gòu)造函數(shù),對(duì)內(nèi)置類(lèi)型成員淺拷貝
  4. 一個(gè)類(lèi)一般只需要一個(gè)拷貝構(gòu)造函數(shù)
  5. 有資源申請(qǐng),一定要顯式定義

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

6.1 運(yùn)算符重載

內(nèi)置類(lèi)型可以直接使用運(yùn)算符,那么自定義類(lèi)型是否能使用運(yùn)算符呢?

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

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

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

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

//private:
	int _year;
	int _month;
	int _day;
};

bool operator==(const Date& d1, const Date& d2)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}

int main()
{
	Date d1;
	Date d2(d1);
	
	operator==(d1, d2);//一般不這樣寫(xiě)
	d1 == d2;//等價(jià)于上述函數(shù)調(diào)用
	cout << (d1 == d2) << endl;
	return 0;
}

這里,我們是直接將private取消,才讓外部函數(shù)可以訪(fǎng)問(wèn)類(lèi)的內(nèi)置類(lèi)型。但是,這樣封裝性又得不到保證了。

所以,我們可以把運(yùn)算符重載定義為成員函數(shù)。

class Date
{
public:
	Date(int year = 1, 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& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1;
	Date d2(d1);
	
	d1.operator==(d2);//一般不這樣寫(xiě)
	d1 == d2;//等價(jià)于上述寫(xiě)法
	return 0;
}

注意

  • 不能通過(guò)連接其他符號(hào)來(lái)創(chuàng)建新的操作符:比如operator@
  • 重載操作符必須有一個(gè)類(lèi)類(lèi)型參數(shù)
  • 用于內(nèi)置類(lèi)型的運(yùn)算符,其含義不能改變,例如:內(nèi)置的整型+,不能改變其含義
  • 作為類(lèi)成員函數(shù)重載時(shí),其形參看起來(lái)比操作數(shù)數(shù)目少1,因?yàn)槌蓡T函數(shù)的第一個(gè)參數(shù)為隱藏的this
  • .* 、::、 sizeof?: 、 . 注意以上5個(gè)運(yùn)算符不能重載。這個(gè)經(jīng)常在筆試選擇題中出現(xiàn)。

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

前面講完了運(yùn)算符重載的前置知識(shí),那么我們就來(lái)實(shí)現(xiàn)d1 = d2這樣的賦值操作

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

  1. 參數(shù)類(lèi)型:const 類(lèi)名&,傳遞引用可以提高傳參效率
  2. 返回值類(lèi)型:類(lèi)名&,返回引用可以提高返回的效率,有返回值目的是為了支持連續(xù)賦值
  3. 檢測(cè)是否自己給自己賦值
  4. 返回*this :要符合連續(xù)賦值的含義
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(2022, 10, 24);
	Date d2(2023, 5, 3);
	d1 = d2;
	return 0;
}

但是,要注意到賦值運(yùn)算符的特殊場(chǎng)景

  1. 連續(xù)賦值,比如d3 = d2 = d1
  2. 原地賦值,比如d1 = d1

所以,我們有以下改進(jìn)版本:

Date& operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	return *this;
}

  • 同樣,若未顯式定義,默認(rèn)生成的賦值重載,對(duì)自定義類(lèi)型成員調(diào)用對(duì)應(yīng)的賦值重載,對(duì)于內(nèi)置類(lèi)型成員淺拷貝。
  • 所以,如果一旦涉及到資源申請(qǐng)時(shí),則賦值重載是一定要顯式定義的,否則就是淺拷貝。

七、日期類(lèi)的實(shí)現(xiàn)

注意:

  • 日期+日期沒(méi)有意義,但是日期-日期有意義,代表間隔的天數(shù)
  • 日期±天數(shù)都有意義,代表往前/后天數(shù)的日期
  • 為了與前置++/- -區(qū)分,后置++/- -聲明參數(shù)類(lèi)型為int(無(wú)意義,僅用于占位符,以作區(qū)分)
  • 這里重載了<<(流插入)和>>(流提?。┎僮鞣\(yùn)用了友元(后續(xù)會(huì)講)

date.h

#pragma once
#include<iostream>
using namespace std;

class Date
{
	friend ostream& operator<<(ostream& out, const Date& d)
	{
		out << d._year << "/" << d._month << "/" << d._day << endl;
		return out;
	}
	friend istream& operator>>(istream& in, Date& d)
	{
		in >> d._year >> d._month >> d._day;
		return in;
	}

public:
	Date(int year = 1, int month = 1, int day = 1);
	void Print();
	bool operator==(const Date& d);
	bool operator!=(const Date& d);

	bool operator<(const Date& d);
	bool operator<=(const Date& d);
	bool operator>(const Date& d);
	bool operator>=(const Date& d);

	Date& operator=(const Date& d);

	Date& operator+=(int day);
	Date operator+(int day);
	Date& operator-=(int day);
	Date operator-(int day);

	int operator-(const Date& d);

	Date& operator++();
	Date operator++(int);
	Date& operator--();
	Date operator--(int);

private:
	int _year;
	int _month;
	int _day;
};

date.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"date.h"

int GetMonthDay(int year, int month)
{
	int a[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))
	{
		return 29;
	}
	return a[month];
}

Date::Date(int year, int month, int day)
{
	if((month > 0 && month <= 12) && (day > 0 && day <= GetMonthDay(year, month)))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout<<"日期非法"<<endl;
	}
}

void Date::Print()
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

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

bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

bool Date::operator<(const Date& d)
{
	return _year < d._year
		|| (_year == d._year && _month < d._month)
		|| (_year == d._year && _month == d._month && _day < d._day);
}

bool Date::operator<=(const Date& d)
{
	return (*this == d) || (*this < d);
}

bool Date::operator>(const Date& d)
{
	return !(*this <= d);
}

bool Date::operator>=(const Date& d)
{
	return !(*this < d);
}

Date& Date::operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	return *this;
}

//d1+=100
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		*this -= -day;
		return *this;
	}

	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}

	return *this;
}

//d1+100
Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp += day;
	return tmp;
}

//d1-=100
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		*this += -day;
		return *this;
	}

	_day -= day;
	while (_day <= 0)
	{
		_day += GetMonthDay(_year, _month);
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
	}

	return *this;
}

//d1-100
Date Date::operator-(int day)
{
	Date tmp(*this);
	tmp -= day;
	return tmp;
}

//d1-d2
int Date::operator-(const Date& d)
{
	Date max = *this;
	Date min = d;
	int flag = 1;

	if (max < min)
	{
		max = d;
		min = *this;
		flag = -1;
	}

	int i = 0;
	while (min != max)
	{
		++min;
		++i;
	}

	return i * flag;
}

//++d1
Date& Date::operator++()
{
	*this += 1;
	return *this;
}

//d1++
Date Date::operator++(int)
{
	Date tmp(*this);
	*this += 1;
	return tmp;
}

//--d1
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}

//d1--
Date Date::operator--(int)
{
	Date tmp(*this);
	*this -= 1;
	return tmp;
}

八、const成員函數(shù)

8.1 概念

將const修飾的“成員函數(shù)”稱(chēng)之為const成員函數(shù),const修飾類(lèi)成員函數(shù),實(shí)際修飾該成員函數(shù)隱含的this指針,表明在該成員函數(shù)中不能對(duì)類(lèi)的任何成員進(jìn)行修改。

舉個(gè)例子:

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

void Func(const Date& d)
{
	d.Print();//err
}

int main()
{
	Date d1;
	Func(d1);
	return 0;
}

分析:上述代碼在Func函數(shù)內(nèi)部(常引用,const修飾),調(diào)用Print函數(shù)(this指針沒(méi)有const修飾),造成權(quán)限的放大,所以編譯器報(bào)錯(cuò)。

8.2 使用方式

那么,我們應(yīng)該怎么做呢?

我們無(wú)法顯式修飾const,所以語(yǔ)法規(guī)定了一種寫(xiě)法——在成員函數(shù)名后接const

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

【C++練級(jí)之路】【Lv.3】類(lèi)和對(duì)象(中)(沒(méi)掌握類(lèi)的6個(gè)默認(rèn)成員函數(shù),那你根本就沒(méi)學(xué)過(guò)C++?。?進(jìn)擊的C++,c++,開(kāi)發(fā)語(yǔ)言,java
這樣,編譯器就會(huì)自動(dòng)對(duì)this指針進(jìn)行const修飾。

注意:只對(duì)功能不改變對(duì)象本身的成員函數(shù),進(jìn)行const修飾。

那么請(qǐng)思考下面的幾個(gè)問(wèn)題:

  1. const對(duì)象可以調(diào)用非const成員函數(shù)嗎?
  2. 非const對(duì)象可以調(diào)用const成員函數(shù)嗎?
  3. const成員函數(shù)內(nèi)可以調(diào)用其它的非const成員函數(shù)嗎?
  4. 非const成員函數(shù)內(nèi)可以調(diào)用其它的const成員函數(shù)嗎?

8.3 日期類(lèi)(const修飾版)

下面是日期類(lèi)實(shí)現(xiàn)中,const修飾后的版本:

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1);
	void Print() const;
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;

	bool operator<(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;

	Date& operator=(const Date& d);

	Date& operator+=(int day);
	Date operator+(int day) const;
	Date& operator-=(int day);
	Date operator-(int day) const;

	int operator-(const Date& d) const;

	Date& operator++();
	Date operator++(int);
	Date& operator--();
	Date operator--(int);

private:
	int _year;
	int _month;
	int _day;
};

注意:聲明與定義分離時(shí),兩邊都要加上const修飾

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

這兩個(gè)默認(rèn)成員函數(shù)一般不用重新定義 ,編譯器默認(rèn)會(huì)生成。

class Date
{
public:
	Date* operator&()
	{
		return this;
	}
	const Date* operator&() const
	{
		return this;
	}
private:
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

int main()
{
	Date d1;
	cout << &d1 << endl;
	const Date d2;
	cout << &d2 << endl;
	return 0;
}

這兩個(gè)運(yùn)算符一般不需要重載,使用編譯器生成的默認(rèn)取地址的重載即可,只有特殊情況,才需要重載,比如想讓別人獲取到指定的內(nèi)容!

總結(jié)

本節(jié)學(xué)習(xí)了類(lèi)的6個(gè)默認(rèn)成員函數(shù),詳細(xì)了解其中的特性與使用方法。

  • 其中構(gòu)造函數(shù),析構(gòu)函數(shù),拷貝構(gòu)造函數(shù)和賦值運(yùn)算符重載,才是我們需要重點(diǎn)掌握并且經(jīng)常要顯式定義的。
    • 默認(rèn)生成的構(gòu)造/析構(gòu)函數(shù),對(duì)其自定義類(lèi)型成員調(diào)用對(duì)應(yīng)的構(gòu)造/析構(gòu)函數(shù),對(duì)內(nèi)置類(lèi)型成員不做處理
    • 默認(rèn)生成的拷貝構(gòu)造/賦值重載函數(shù),對(duì)其自定義類(lèi)型成員調(diào)用對(duì)應(yīng)的拷貝構(gòu)造/賦值重載函數(shù),對(duì)內(nèi)置類(lèi)型成員淺拷貝
  • 至于取地址運(yùn)算符重載,比較簡(jiǎn)單,且平常不用顯式定義。

看到這里了還不給博主扣個(gè):
?? 點(diǎn)贊??收藏 ?? 關(guān)注!
?? ?? ?? ?? ???? ?? ?? ?? ?? ??
拜托拜托這個(gè)真的很重要!
你們的點(diǎn)贊就是博主更新最大的動(dòng)力!
有問(wèn)題可以評(píng)論或者私信呢秒回哦。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-761390.html

到了這里,關(guān)于【C++練級(jí)之路】【Lv.3】類(lèi)和對(duì)象(中)(沒(méi)掌握類(lèi)的6個(gè)默認(rèn)成員函數(shù),那你根本就沒(méi)學(xué)過(guò)C++!)的文章就介紹完了。如果您還想了解更多內(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++】類(lèi)和對(duì)象②(類(lèi)的默認(rèn)成員函數(shù):構(gòu)造函數(shù) | 析構(gòu)函數(shù))

    【C++】類(lèi)和對(duì)象②(類(lèi)的默認(rèn)成員函數(shù):構(gòu)造函數(shù) | 析構(gòu)函數(shù))

    ?? 個(gè)人主頁(yè): Forcible Bug Maker ?? 專(zhuān)欄: C++ 目錄 前言 類(lèi)的6個(gè)默認(rèn)成員函數(shù) 構(gòu)造函數(shù) 概念 構(gòu)造函數(shù)的特性及用法 析構(gòu)函數(shù) 概念 析構(gòu)函數(shù)的特性及用法 結(jié)語(yǔ) 本篇主要內(nèi)容:類(lèi)的6個(gè)默認(rèn)成員函數(shù)中的 構(gòu)造函數(shù) 和 析構(gòu)函數(shù) 進(jìn)入到類(lèi)和對(duì)象內(nèi)容的第二節(jié),上篇博客中介紹了

    2024年04月16日
    瀏覽(37)
  • 【C++練級(jí)之路】【Lv.6】【STL】string類(lèi)的模擬實(shí)現(xiàn)

    歡迎各位小伙伴關(guān)注我的專(zhuān)欄,和我一起系統(tǒng)學(xué)習(xí)C語(yǔ)言,共同探討和進(jìn)步哦! 學(xué)習(xí)專(zhuān)欄 : 《進(jìn)擊的C++》 關(guān)于 STL容器 的學(xué)習(xí),我會(huì)采用 模擬實(shí)現(xiàn) 的方式,以此來(lái)更加清楚地了解其 底層原理和整體架構(gòu) 。而string類(lèi)更是有100多個(gè)接口函數(shù),所以模擬實(shí)現(xiàn)的時(shí)候只會(huì)調(diào)重點(diǎn)和

    2024年01月18日
    瀏覽(30)
  • 【C++練級(jí)之路】【Lv.7】【STL】vector類(lèi)的模擬實(shí)現(xiàn)

    【C++練級(jí)之路】【Lv.7】【STL】vector類(lèi)的模擬實(shí)現(xiàn)

    快樂(lè)的流暢:個(gè)人主頁(yè) 個(gè)人專(zhuān)欄:《C語(yǔ)言》《數(shù)據(jù)結(jié)構(gòu)世界》《進(jìn)擊的C++》 遠(yuǎn)方有一堆篝火,在為久候之人燃燒! 關(guān)于STL容器的學(xué)習(xí),我們來(lái)到了運(yùn)用 最廣泛、最常見(jiàn)的vector 。有了之前關(guān)于string的學(xué)習(xí),我們對(duì)容器設(shè)計(jì)有了一個(gè)大概的了解,而今天在熟悉的基礎(chǔ)上去探求

    2024年01月24日
    瀏覽(35)
  • 【C++精華鋪】5.C++類(lèi)和對(duì)象(中)類(lèi)的六個(gè)默認(rèn)成員函數(shù)

    【C++精華鋪】5.C++類(lèi)和對(duì)象(中)類(lèi)的六個(gè)默認(rèn)成員函數(shù)

    目錄 1. 六個(gè)默認(rèn)成員函數(shù) 2. 構(gòu)造函數(shù) 2.1 概念 2.2 默認(rèn)構(gòu)造 2.2.1 系統(tǒng)生成的默認(rèn)構(gòu)造 2.2.2 自定義默認(rèn)構(gòu)造函數(shù) ?2.3 構(gòu)造函數(shù)的重載 3. 析構(gòu)函數(shù) 3.1 概念 ?3.2 系統(tǒng)生成的析構(gòu)函數(shù) ?3.3 自定義析構(gòu)函數(shù) 4. 拷貝構(gòu)造 4.1 概念 ?4.2 默認(rèn)生成的拷貝構(gòu)造(淺拷貝) ?4.3 自定義拷貝構(gòu)

    2024年02月13日
    瀏覽(52)
  • 【C++】類(lèi)和對(duì)象③(類(lèi)的默認(rèn)成員函數(shù):拷貝構(gòu)造函數(shù) | 賦值運(yùn)算符重載)

    【C++】類(lèi)和對(duì)象③(類(lèi)的默認(rèn)成員函數(shù):拷貝構(gòu)造函數(shù) | 賦值運(yùn)算符重載)

    ?? 個(gè)人主頁(yè): Forcible Bug Maker ?? 專(zhuān)欄: C++ 目錄 前言 拷貝構(gòu)造函數(shù) 概念 拷貝構(gòu)造函數(shù)的特性及用法 賦值運(yùn)算符重載 運(yùn)算符重載 賦值運(yùn)算符重載 結(jié)語(yǔ) 本篇主要內(nèi)容:類(lèi)的6個(gè)默認(rèn)成員函數(shù)中的 拷貝構(gòu)造函數(shù) 和 賦值運(yùn)算符重載 在上篇文章中我們講到了類(lèi)的默認(rèn)成員函數(shù)的

    2024年04月17日
    瀏覽(29)
  • 【C++】類(lèi)和對(duì)象④(類(lèi)的默認(rèn)成員函數(shù):取地址及const取地址重載 | 再談構(gòu)造函數(shù):初始化列表,隱式類(lèi)型轉(zhuǎn)換,缺省值)

    【C++】類(lèi)和對(duì)象④(類(lèi)的默認(rèn)成員函數(shù):取地址及const取地址重載 | 再談構(gòu)造函數(shù):初始化列表,隱式類(lèi)型轉(zhuǎn)換,缺省值)

    ?? 個(gè)人主頁(yè): Forcible Bug Maker ?? 專(zhuān)欄: C++ 目錄 前言 取地址及const取地址操作符重載 再談構(gòu)造函數(shù) 初始化列表 隱式類(lèi)型轉(zhuǎn)換 explicit 成員變量缺省值 結(jié)語(yǔ) 本篇主要內(nèi)容:類(lèi)的六個(gè)默認(rèn)成員函數(shù)中的 取地址 及 const取地址重載 , 構(gòu)造函數(shù) 初始化列表 , 隱式類(lèi)型轉(zhuǎn)換

    2024年04月26日
    瀏覽(30)
  • 【C++打怪之路Lv3】-- 類(lèi)和對(duì)象(上)

    【C++打怪之路Lv3】-- 類(lèi)和對(duì)象(上)

    ?? 個(gè)人主頁(yè):白子寰 ?? 分類(lèi)專(zhuān)欄: C++打怪之路,python從入門(mén)到精通,數(shù)據(jù)結(jié)構(gòu),C語(yǔ)言,C語(yǔ)言題集 ?? 希望得到您的訂閱和支持~ ?? 堅(jiān)持創(chuàng)作博文(平均質(zhì)量分82+),分享更多關(guān)于深度學(xué)習(xí)、C/C++,python領(lǐng)域的優(yōu)質(zhì)內(nèi)容?。ㄏM玫侥年P(guān)注~)? ? 目錄 面向?qū)ο蠛兔嫦蜻^(guò)程

    2024年04月27日
    瀏覽(33)
  • C++ ------類(lèi)和對(duì)象詳解六大默認(rèn)成員函數(shù)

    C++ ------類(lèi)和對(duì)象詳解六大默認(rèn)成員函數(shù)

    如果我們定義一個(gè)類(lèi),然后這個(gè)類(lèi)中什么也沒(méi)有。那么這里的類(lèi)就什么也沒(méi)有嗎?其實(shí)不然,任何類(lèi)在里面什么都不寫(xiě)時(shí),編譯器都會(huì)生成6個(gè)默認(rèn)成員函數(shù)。 用戶(hù)沒(méi)有顯式實(shí)現(xiàn),編譯器會(huì)生成的成員函數(shù)稱(chēng)為默認(rèn)成員函數(shù)。 六個(gè)默認(rèn)成員函數(shù) 我們來(lái)看一個(gè)Date類(lèi) 觀察上述代

    2024年02月15日
    瀏覽(28)
  • 【C++】類(lèi)和對(duì)象(中篇)----->六大默認(rèn)成員函數(shù)

    【C++】類(lèi)和對(duì)象(中篇)----->六大默認(rèn)成員函數(shù)

    目錄 一、類(lèi)的6個(gè)默認(rèn)成員函數(shù) 二、構(gòu)造函數(shù) ?1、概念? ?2、特性 三、析構(gòu)函數(shù) ?1、概念 ?2、特性 四、拷貝構(gòu)造函數(shù) ?1、概念 ?2、特征 五、賦值運(yùn)算符重載 ?1、運(yùn)算符重載 ?2、值運(yùn)算符重載 ? ?2.1?賦值運(yùn)算符重載格式 ? ?2.2?賦值運(yùn)算符只能重載成類(lèi)的成員函數(shù)不能

    2024年02月12日
    瀏覽(26)
  • 【C++類(lèi)和對(duì)象】類(lèi)有哪些默認(rèn)成員函數(shù)呢?(下)

    【C++類(lèi)和對(duì)象】類(lèi)有哪些默認(rèn)成員函數(shù)呢?(下)

    ヾ(????)?\\\" 人總要為過(guò)去的懶惰而付出代價(jià) ヾ(????)?\\\" 如果一個(gè)類(lèi)中什么成員都沒(méi)有,簡(jiǎn)稱(chēng)為空類(lèi)。 空類(lèi)中并不是什么都沒(méi)有,任何類(lèi)在什么都不寫(xiě)時(shí),編譯器會(huì)自動(dòng)生成以下6個(gè)默認(rèn)成員函數(shù)。 ???????? 默認(rèn)成員函數(shù):用戶(hù)沒(méi)有顯式實(shí)現(xiàn),編譯器會(huì)生成

    2024年02月12日
    瀏覽(33)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包