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

C++好難(3):類和對象(中篇)

這篇具有很好參考價(jià)值的文章主要介紹了C++好難(3):類和對象(中篇)。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

【本章目標(biāo)】

  • 類的6個(gè)默認(rèn)成員函數(shù)
  • 構(gòu)造函數(shù)
  • 析構(gòu)函數(shù)
  • 拷貝構(gòu)造函數(shù)
  • 賦值運(yùn)算符重載
  • const成員函數(shù)
  • 取地址及const取地址操作符重載

目錄

【本章目標(biāo)】

1.類的6個(gè)默認(rèn)成員函數(shù)

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

2.1概念

2.2構(gòu)造函數(shù)的特性

特性一

特性二

特性三

特性四

特性五

特性六

特性七

2.3總結(jié)

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

3.1概念:

3.2特性

特性一

特性二

特性三

特性四

特性五

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

4.1概念

4.2拷貝構(gòu)造的特性

特性一

特性二

特性三

特性四:

4.3 總結(jié)

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

5.1運(yùn)算符重載

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

5.3賦值運(yùn)算符重載的一個(gè)特性

6. const 成員

6.1const成員函數(shù)

6.2思考

7.取地址及const取地址操作符重載

練習(xí)


1.類的6個(gè)默認(rèn)成員函數(shù)

C++好難(3):類和對象(中篇)?空類里面不是什么都沒有,而是會自動(dòng)生成上面6個(gè)默認(rèn)成員函數(shù)

主要:

這6個(gè)默認(rèn)的成員函數(shù)是“缺省”的,你不寫這6個(gè)函數(shù),編譯器就會自動(dòng)生成,但是你如果寫了某一個(gè),那編譯器就不會生成了

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

2.1概念

對于以下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, 3, 1);
	d1.Print();

	Date d2;
	d2.Init(2023, 5, 1);
	d2.Print();
	return 0;
}

這個(gè)Date類可以通過 Init 公有方法給對象設(shè)置日期,但如果每次創(chuàng)建對象時(shí)都調(diào)用該方法設(shè)置信息,未免有點(diǎn)麻煩,那能否在對象創(chuàng)建時(shí),就將信息設(shè)置進(jìn)去呢?

這時(shí)候就需要用到我們的構(gòu)造函數(shù)

2.2構(gòu)造函數(shù)的特性

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

特性一

(1)構(gòu)造函數(shù)的函數(shù)名與類名相同

C++好難(3):類和對象(中篇)

特性二

(2)構(gòu)造函數(shù)無返回值,這里指的是返回值不用寫,而不是void

特性三

(3)對象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對應(yīng)的構(gòu)造函數(shù)

當(dāng)你用類創(chuàng)建一個(gè)對象時(shí),編譯器會自動(dòng)調(diào)用該類的構(gòu)造函數(shù)對新創(chuàng)建的變量進(jìn)行初始化。

特性四

(4)構(gòu)造函數(shù)可以重載

這就意味著我們可以寫很多不同初始化的構(gòu)造函數(shù),當(dāng)我們需要那種初始化,我們傳遞對應(yīng)的參數(shù)即可

class Date
{
public:
	//無參的構(gòu)造函數(shù)
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	//帶參的構(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)用無參的構(gòu)造函數(shù)
	d1.Print();

	Date d2(2023, 1, 1);//調(diào)用有參的構(gòu)造函數(shù)
	d2.Print();

	return 0;
}

注意:在通過無參構(gòu)造函數(shù)創(chuàng)建對象時(shí)對象后面不用加括號,否則就是函數(shù)聲明

特性五

(5)默認(rèn)構(gòu)造函數(shù)

無參構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個(gè)

class Date
{
public:
	//全缺省的構(gòu)造函數(shù)
	Date(int year = 2023, int month = 5, 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;
	d1.Print();

	Date d2(2023, 1, 1);
	d2.Print();
	return 0;
}

d1沒有傳參,會直接調(diào)用構(gòu)造函數(shù)的缺省值,d2會進(jìn)行賦值操作

特性六

(6)如果類中沒有顯式定義構(gòu)造函數(shù),則C++編譯器會自動(dòng)生成一個(gè)無參的默認(rèn)構(gòu)造函數(shù),一旦用戶顯式定義編譯器將不再生成。

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

int main()
{
	Date d1; // 此處調(diào)用的是編譯器生成的默認(rèn)構(gòu)造函數(shù)
	//可以看到我們沒有定義構(gòu)造函數(shù),對象也可以創(chuàng)建成功,
	d1.Print();

	return 0;
}

打印結(jié)果:

C++好難(3):類和對象(中篇)

我們可以看到d1調(diào)用了編譯器創(chuàng)建的默認(rèn)構(gòu)造函數(shù),單初始化結(jié)構(gòu)卻是 -858993460,
而不是初始化為0

這時(shí)候就有人想說:這編譯器生成的默認(rèn)構(gòu)造函數(shù)有個(gè)√8用?

這是因?yàn)榫幾g器有一套自動(dòng)生成的構(gòu)造函數(shù)機(jī)制:

  • 編譯器自動(dòng)生成的構(gòu)造函數(shù)對內(nèi)置類型不做處理
  • 對自定義類型,編譯器會再去調(diào)用他們自己的構(gòu)造函數(shù)

內(nèi)置類型就是編譯器創(chuàng)建的基本類型,如int,char等等

自定義類型就是class、struct定義的類對象

如下代碼:

class Week
{
public:
	Week()
	{
		cout << " Week()" << endl;
		_week = 1;
	}
private:
	int _week;
};

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
    // 內(nèi)置類型
	int _year; // 年
	int _month; // 月
	int _day; // 日

	// 自定義類型
	Week w1;
};

int main()
{
	Date d1;
	d1.Print();

	return 0;
}

調(diào)用結(jié)果:

C++好難(3):類和對象(中篇)

可以看到:

默認(rèn)生成的構(gòu)造函數(shù)堆內(nèi)置類型成員變量不做處理,
對于自定義類型成員變量才會處理(去調(diào)用他們自己的默認(rèn)構(gòu)造函數(shù))

特性七

(7)如果一個(gè)類中的成員全是自定義類型,我們就可以用默認(rèn)生成的構(gòu)造函數(shù);
如果有內(nèi)置類型的成員,或者需要顯示傳參初始化,那么都要自己實(shí)現(xiàn)構(gòu)造函數(shù)。

2.3總結(jié)

默認(rèn)構(gòu)造函數(shù)有三種:

  1. 我們不寫,讓編譯器自動(dòng)生成的
  2. 我們自己寫的? 無參? 的構(gòu)造函數(shù)
  3. 我們自己寫的? 全缺省? 的構(gòu)造函數(shù)

雖然我們在不寫的情況下,編譯器會自動(dòng)生成構(gòu)造函數(shù),但是編譯器自動(dòng)生成的構(gòu)造函數(shù)可能達(dá)不到我們想要的效果,

所以大多數(shù)情況下都需要我們自己寫構(gòu)造函數(shù),并且最好是寫全缺省的構(gòu)造函數(shù)

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

3.1概念:

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

3.2特性

析構(gòu)函數(shù)和構(gòu)造函數(shù)一樣,也是一個(gè)特殊的成員函數(shù)

我們都知道,當(dāng)一個(gè)類對象銷毀時(shí),其中的局部表里也會是隨著該對象的銷毀而銷毀

我們通常會寫一個(gè)destroy函數(shù)來進(jìn)行銷毀操作

而析構(gòu)函數(shù)就相當(dāng)于destroy函數(shù)的作用

class Stack
{
public:
	// 構(gòu)造函數(shù)
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}

	// 析構(gòu)函數(shù)
	~Stack()
	{
		free(_a);
		_a = nullptr;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st;
	return 0;
}

在數(shù)據(jù)結(jié)構(gòu)中,我們實(shí)現(xiàn)棧時(shí)都會寫一個(gè)Destroy函數(shù)再程序結(jié)束前銷毀動(dòng)態(tài)開辟的內(nèi)存,
而如果我們在使用完后沒有及時(shí)銷毀,那么就可能導(dǎo)致內(nèi)存泄露的問題

而析構(gòu)函數(shù)的出現(xiàn)就是為了防止我們忘記銷毀,對象實(shí)例化后,同構(gòu)造函數(shù)一樣,它不需要我們主動(dòng)調(diào)用,它是在對象生命周期結(jié)束后自動(dòng)調(diào)用,需要注意的是,析構(gòu)函數(shù)沒有參數(shù)所以不能重載。

特性一

(1)析構(gòu)函數(shù)的名是在類名前面加上字符? ??~??

class Date
{
public:
	// 構(gòu)造函數(shù)
	Date()
	{

	}

	// 析構(gòu)函數(shù)
	~Date()
	{

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

特性二

(2)析構(gòu)函數(shù)無參數(shù)不能重載,無返回值

特性三

(3)對象聲明周期結(jié)束時(shí),編譯器會自動(dòng)第哦啊用析構(gòu)函數(shù)

如下代碼:

class Date
{
public:
	// 全缺省的構(gòu)造函數(shù)
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

	// 析構(gòu)函數(shù)
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 5, 1);
	d1.Print();

	return 0;
}

編譯結(jié)果:

C++好難(3):類和對象(中篇)

可以看到我們沒有調(diào)用~Date()這個(gè)函數(shù),但是編譯器是會默認(rèn)調(diào)用這個(gè)析構(gòu)函數(shù)的

特性四

(4)一個(gè)類有且只有一個(gè)析構(gòu)函數(shù)。若未顯示定義,系統(tǒng)會自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)

  • 編譯器自動(dòng)生成的析構(gòu)函數(shù)對內(nèi)置類型不做處理
  • 對于自定義類型,編譯器會再去調(diào)用他們自己的析構(gòu)函數(shù)

和構(gòu)造函數(shù)一樣

特性五

(5)先構(gòu)造的后析構(gòu),后構(gòu)造的先析構(gòu)

析構(gòu)函數(shù)的析構(gòu)順序和棧一樣,先進(jìn)后出

C++好難(3):類和對象(中篇)

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

4.1概念

在我們編寫代碼的過程中,免不了要對以一個(gè)類對象進(jìn)行拷貝,生成一個(gè)新的和原來一樣的對象

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

來看下面的代碼:

class Date
{
public:
	//構(gòu)造函數(shù)
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//拷貝構(gòu)造
	Date(const Date& d) 
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

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

int main()
{
	Date d1(2023, 5, 1);
	Date d2(d1); // 用已存在的對象d1創(chuàng)建對象d2

	return 0;
}

我們這里對象 d2 就是對 對象 d1 的拷貝構(gòu)造,通過調(diào)試我們就可以看到:

C++好難(3):類和對象(中篇)

4.2拷貝構(gòu)造的特性

特性一

(1)拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式

因?yàn)榭截悩?gòu)造函數(shù)的函數(shù)名也與類名相同

特性二

(2)拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須用引用傳參

傳值傳參會發(fā)生錯(cuò)誤,無限遞歸的錯(cuò)誤

傳值傳參為什么會發(fā)生無限遞歸呢?

C++好難(3):類和對象(中篇)

?當(dāng)進(jìn)行傳值傳參的時(shí)候,我們要調(diào)用? 拷貝構(gòu)造函數(shù)? 就需要先? 傳參,這時(shí)的傳參是傳值傳參,在傳參的過程中又需要進(jìn)行對象的拷貝構(gòu)造,如此循環(huán)發(fā)生無限遞歸。

這里用引用來解決了問題,加const的原因是因?yàn)槲覀兣掠行┤藢戝e(cuò)順序,導(dǎo)致原來的d被改變

//拷貝構(gòu)造
Date(const Date& d) 
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

C++好難(3):類和對象(中篇)

特性三

(3)若未進(jìn)行顯示定義,系統(tǒng)生成默認(rèn)拷貝構(gòu)造函數(shù)

系統(tǒng)生成的默認(rèn)拷貝構(gòu)造函數(shù)對象,按內(nèi)存存儲字節(jié)完成拷貝,
這種拷貝我們叫做淺拷貝(或值拷貝)

如下代碼所示:

class Date
{
public:
	// 構(gòu)造函數(shù)
	Date(int year = 0, 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(2023, 5, 1);
	Date d2(d1); // 用已存在的對象d1創(chuàng)建對象d2

	d1.Print();
	d2.Print();
	return 0;
}

運(yùn)行結(jié)果:

C++好難(3):類和對象(中篇)

在上述代碼中,我們沒有編寫拷貝構(gòu)造函數(shù),但編譯器通過自動(dòng)生成的拷貝構(gòu)造函數(shù)完成了d2對d1的拷貝

這就要談?wù)劸幾g器自動(dòng)生成拷貝構(gòu)造函數(shù)的機(jī)制:

  • 編譯器自動(dòng)生成的拷貝構(gòu)造函數(shù)對內(nèi)置類型會完成淺拷貝(值拷貝)
  • ?對于自定義類型,編譯器會再去調(diào)用他們自己的默認(rèn)拷貝構(gòu)造函數(shù)

特性四:

(4)編譯器自動(dòng)生成的拷貝構(gòu)造函數(shù)不能實(shí)現(xiàn)?深拷貝?

什么是? 淺拷貝? 什么是? 深拷貝?

淺拷貝:對值進(jìn)行拷貝,不會另外開空間

深拷貝:會另開辟一塊空間,再將需要拷貝的值放入該空間中

顯然,對于日期這樣的類是沒有必要進(jìn)行深拷貝的,但對于一些復(fù)雜的數(shù)據(jù)結(jié)構(gòu),我們就不能再進(jìn)行淺拷貝了,否則會出現(xiàn)指向同一區(qū)域的錯(cuò)誤

如下代碼:

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_data = (int*)malloc(sizeof(int) * capacity);
		if (_data == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		free(_data);
		_data = nullptr;
	}
private:
	int* _data;
	int _top;
	int _capacity;
};

int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

我們用的是編譯器自動(dòng)生成的拷貝構(gòu)造函數(shù),進(jìn)行調(diào)試我們可以看到:st2已經(jīng)完成了對st1的拷貝

C++好難(3):類和對象(中篇)

但是程序在運(yùn)行的時(shí)候還是崩潰了

C++好難(3):類和對象(中篇)

原因就是進(jìn)行了淺拷貝的問題!

我們仔細(xì)看上述調(diào)試結(jié)構(gòu),會發(fā)現(xiàn)st1和st2指向的地址是同一塊地址,這就說明再進(jìn)行拷貝的時(shí)候,st2并沒有開辟新的空間

C++好難(3):類和對象(中篇)

這時(shí)我們不管對他們兩個(gè)哪一個(gè)做出該改變,都會影響到另一個(gè)

如果在我們自己定義的析構(gòu)函數(shù)是正確的情況下,當(dāng)程序運(yùn)行結(jié)束,st2 棧將被先析構(gòu),然后再去析構(gòu)st1

而此時(shí),st2析構(gòu)的時(shí)候?st2 和 st1 指向同一塊兒空間已經(jīng)被釋放了,那么當(dāng) st1 棧再去調(diào)用析構(gòu)函數(shù)的時(shí)候,會再次對那一塊空間進(jìn)行釋放,造成一塊空間被多次釋放的問題,導(dǎo)致程序崩潰!

顯然這種結(jié)構(gòu)是我們不想看到的,所以再這種復(fù)雜數(shù)據(jù)結(jié)構(gòu)的拷貝過程中,編譯器自動(dòng)生成的拷貝構(gòu)造函數(shù)就不能滿足我們的要求了。

4.3 總結(jié)

我們不寫,編譯器會默認(rèn)生成一個(gè)拷貝構(gòu)造:

(1)內(nèi)置類型的成員會完成值拷貝,也就是淺拷貝。

像 Date 這樣的類,需要的就是淺拷貝,那么編譯器自動(dòng)生成的拷貝構(gòu)造函數(shù)就夠用了,我們不需要自己寫。

(2)自定義類型的成員,去調(diào)用這個(gè)成員的拷貝構(gòu)造

像 Stack 這樣的類,它是自己直接管理資源,那么需要自己實(shí)現(xiàn)深拷貝,淺拷貝的話會導(dǎo)致析構(gòu)兩次、程序崩潰的問題。

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

運(yùn)算符重載和函數(shù)重載是不一樣的

  • 函數(shù)重載:支持函數(shù)名相同、參數(shù)不同的函數(shù),可以同時(shí)使用
  • 運(yùn)算符重載:自定義類型對象可以使用運(yùn)算符

5.1運(yùn)算符重載

創(chuàng)建一個(gè)日期Date類,現(xiàn)在要判斷對象d1和對象d2是否相等要如何來做呢?

class Date
{
public:
	//構(gòu)造函數(shù)
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 5, 1);
	Date d2(2023, 3, 1);

	return 0;
}

如果我們直接這樣寫:

C++好難(3):類和對象(中篇)

肯定是錯(cuò)誤的,可以看到編譯器報(bào)的錯(cuò)誤是沒有與Date類型匹配的運(yùn)算符

這時(shí)因?yàn)?運(yùn)算符默認(rèn)都是個(gè)內(nèi)置類型使用的。自定義類型的變量用這些運(yùn)算符,得自己編寫運(yùn)算符重載函數(shù)來實(shí)現(xiàn)

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

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

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

注意:

  • 不能通過連接其他符號來創(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)算符不能重載。這個(gè)經(jīng)常在筆試選擇題中出現(xiàn)

下面以? d1==d2為例,通過代碼的形式來學(xué)習(xí)運(yùn)算符重載

class Date
{
public:
	Date(int year = 2023, int month = 1, int day = 1) // 構(gòu)造函數(shù)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//==的運(yùn)算符重載函數(shù)
	bool operator==(const Date& d)  // 這里等價(jià)于 bool operator==(Date* this, const Date& d2)
	{
		// 這里需要注意的是,左操作數(shù)是this指向的調(diào)用函數(shù)的對象

		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 5, 1);
	Date d2(2021, 3, 1);

	//顯示調(diào)用:
	d1.operator==(d2);
	//我們一般就直接這樣寫
	d1 == d2;//同上,編譯器會自動(dòng)識別轉(zhuǎn)換為 d1.operator==(d2) --> d1.operator(&d1, d2);

	cout << (d1 == d2) << endl; 
	// 注意運(yùn)算符重載打印時(shí)要加括號(),因?yàn)檫\(yùn)算符優(yōu)先級的關(guān)系

	return 0;
}

當(dāng)然,我們也可以將運(yùn)算符重載放到類外,但這樣就無妨訪問到收private私有類型保護(hù)的成員變量,我們可以用將成員變量設(shè)置為public的公共類型,這樣當(dāng)然是不好的,等到后面學(xué)習(xí)了友元函數(shù)就可以解決

我們先將成員函數(shù)設(shè)為public來看一看,放到類外的運(yùn)算符重載,代碼應(yīng)該怎么寫

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1) // 構(gòu)造函數(shù)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
//private:    //將成員函數(shù)變?yōu)楣差?	int _year;
	int _month;
	int _day;
};

bool operator==(const Date& d1, const Date& d2)// ==運(yùn)算符重載函數(shù)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}

int main()
{
	Date d1(2023, 5, 1);
	Date d2(2021, 3, 1);

	//顯示調(diào)用
	operator==(d1, d2);

	//一般寫法:
	d1 == d2; //同上,如果沒有重載會報(bào)錯(cuò),如果重載了它會轉(zhuǎn)換為 operator==(d1, d2);

	cout << (d1 == d2) << endl;

	return 0;
}

可以看到在類外時(shí),因?yàn)闆]有 this 指針,所以再顯示調(diào)用函數(shù)時(shí),其形參我們需要設(shè)置兩個(gè)

放到類外的寫法是不推薦的,因?yàn)樗茐牧朔庋b,這里只是為了讓大家看看類內(nèi)寫法和類外寫法的區(qū)別,以及理解隱藏的this參數(shù)

在編寫好運(yùn)算符重載函數(shù)后,無論它是在類內(nèi)還是類外,我們寫的時(shí)候都是直接寫? d1 == d2?

這里我們可以在寫一下其他的比較,比如 < , > , <=,? >= , !=

代碼如下:

class Date
{
public:
	Date(int year = 2023, int month = 1, int day = 1) // 構(gòu)造函數(shù)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//==的運(yùn)算符重載函數(shù)
	bool operator==(const Date& d)  
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}

	// d1 < d2
	bool operator<(const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

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

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

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

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

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

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

賦值運(yùn)算符: =?

賦值運(yùn)算符的重載還是以日期Date類未例:

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

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	//賦值運(yùn)算符重載
	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;
};

至于我們?yōu)槭裁匆@樣寫賦值運(yùn)算符重載,有以下原因:

原因1:

為什么參數(shù)類型要設(shè)置為引用,并用const進(jìn)行修飾?

C++好難(3):類和對象(中篇)

由于是自定義類型傳參,我們?nèi)绻褂?傳值 傳參,會額外調(diào)用一次拷貝構(gòu)造函數(shù),所以函數(shù)的第二個(gè)參數(shù)最好使用 引用傳參(第一個(gè)參數(shù)是默認(rèn)的 this 指針,我們不用管)。

其次,第二個(gè)參數(shù),即賦值運(yùn)算符的右操作數(shù),我們在函數(shù)體內(nèi)不會對其進(jìn)行修改,所以最好加上 const 進(jìn)行修飾。

原因2:

為什么函數(shù)要寫返回值?并且函數(shù)的返回值要使用 引用返回 ?

C++好難(3):類和對象(中篇)

寫返回值是因?yàn)?,在賦值操作的時(shí)候我們經(jīng)常會遇到連續(xù)賦值的場景,如d1 = d2 = d3 = d4

寫返回值就能實(shí)現(xiàn)連續(xù)賦值的情況,更加模擬賦值運(yùn)算符

用引用返回是為了避免不必要的拷貝,適用于出來作用域返回值沒有被銷毀的場景

具體可以看之前我寫的c++入門那篇文章

原因3

為什么要用 if ,他是用來干嘛的?

C++好難(3):類和對象(中篇)

這個(gè) if 條件判斷是用來檢查,賦值是否是給自己賦值的,
因?yàn)槲覀冊谫x值操作時(shí)有時(shí)會寫成:d1 = d1? 的情況

為了防止不必要的操作

原因4

為什么返回的是 *this?

對于? 賦值運(yùn)算?d1 = d2? 的情況,是將d2的值賦值給d1

對其顯示處理就是:d1.operator = (d2)

又因?yàn)?strong>賦值運(yùn)算的計(jì)算順序是從右往左走的,在連續(xù)賦值的時(shí)候就會有所體現(xiàn)

所以我們應(yīng)該返回賦值運(yùn)算的左操作數(shù),而在類中,左操作數(shù)就是this指針

this是d1的地址,返回*this,就是返回d1

5.3賦值運(yùn)算符重載的一個(gè)特性

一個(gè)類如果沒有顯式定義賦值運(yùn)算符重載,編譯器也會生成一個(gè),完成對象按字節(jié)進(jìn)行的值拷貝。

class Date
{
public:
	Date(int year = 2023, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print() // 打印函數(shù)
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2(2023, 5, 1);

	// 這里d1調(diào)用的編譯器生成 operator= 完成拷貝,d2和d1的值也是一樣的。
	d1 = d2;

	d1.Print();
	d2.Print();

	return 0;
}

結(jié)果:

C++好難(3):類和對象(中篇)

可以看到賦值運(yùn)算符重載編譯器也可以自動(dòng)生成,并且也是支持連續(xù)賦值的。

6. const 成員

6.1const成員函數(shù)

我們把 const 修飾的? “成員函數(shù)”? 稱為 const 成員函數(shù)

const修飾類成員函數(shù),實(shí)際修飾該成員函數(shù)隱含的this指針,表明在該成員函數(shù)中不能對類的任何成員進(jìn)行修改。

class Date
{
public:
	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()
{
	const Date d1(2022, 9, 1);//用const修飾對象d1,使其成員在使用時(shí)不會改變
	d1.Print();

	return 0;
}

以上代碼中,我們用const來修飾類對象d1,使得d1里面的成員變量不會再之后的使用中被修改

但是再編譯時(shí)會報(bào)錯(cuò):

C++好難(3):類和對象(中篇)

很明顯我們的Print函數(shù)沒有去該改變成員變量,那為什么還會報(bào)錯(cuò)呢?

這是因?yàn)椋?d1 對象去調(diào)用 Print?函數(shù)的時(shí)候,實(shí)參會把 d1 的地址傳過去,但是 d1 是被 const 修飾的,也就是傳過去的是 const Date* ;

而在 Print1 函數(shù)這邊,形參部分會有一個(gè)隱含的 this 指針,也是 Date* ?this
也就是把?const Date* 傳給了 Date* ?this,在這里屬于權(quán)限的放大,所以編譯會不通過。

?C++好難(3):類和對象(中篇)

那么我們應(yīng)該怎么解決呢?

這里我們就可以用const來修飾成員函數(shù):const 放在成員函數(shù)之后,實(shí)際就是修飾 this 指針:

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

    // const成員函數(shù)
	void Print1() const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	const Date d1(2022, 9, 1);//用const修飾對象d1,使其成員在使用時(shí)不會改變
	d1.Print1();

	return 0;
}

那么在參數(shù)傳遞部分,實(shí)參還是和上面一樣,形參部分因?yàn)?const 修飾的成員函數(shù),所以就變成了 const Date* ?this,那么此時(shí)就是權(quán)限相等了。

C++好難(3):類和對象(中篇)

6.2思考

1. const對象可以調(diào)用非const成員函數(shù)嗎?

?

不可以,就像上面的Print函數(shù)一樣,會報(bào)錯(cuò)

非 const 成員函數(shù),即成員函數(shù)的 this 指針沒有被 const 所修飾,我們傳入一個(gè)被 const 修飾的對象,使用沒有被 const 修飾的 this 指針進(jìn)行接收,屬于權(quán)限的放大,函數(shù)調(diào)用失敗。

2. 非const對象可以調(diào)用const成員函數(shù)嗎?

?

可以,因?yàn)槭菣?quán)限縮小

我們傳入 成員函數(shù)的是一個(gè) 大的,而成員函數(shù)在接受時(shí),由于const的存在,將其 縮小

屬于權(quán)限縮小,可以被執(zhí)行

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

?

不可以,在一個(gè)被 const 所修飾的成員函數(shù)中調(diào)用其他沒有被 const 所修飾的成員函數(shù),

也就是將一個(gè)被 const 修飾的 this 指針的值賦值給一個(gè)沒有被 const 修飾的 this 指針,屬于權(quán)限的放大,函數(shù)調(diào)用失敗。

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

?

可以,在一個(gè)沒有被 const 所修飾的成員函數(shù)中調(diào)用其他被 const 所修飾的成員函數(shù),

也就是將一個(gè)沒有被 const 修飾的 this 指針的值賦值給一個(gè)被 const 修飾的 this 指針,屬于權(quán)限的縮小,函數(shù)調(diào)用成功

7.取地址及const取地址操作符重載

取地址操作符重載 const 取地址操作符重載,這兩個(gè)默認(rèn)成員函數(shù)一般不用自己重新定義,使用編譯器自動(dòng)生成的就行。

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    
    //顯示寫出:取地址操作符重載

	//普通對象 取地址操作符重載
	Date* operator&()
	{
		return this;
	}

	//const對象 取地址操作符重載
	const Date* operator&() const
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 5, 7);
	const Date d2(2023, 5, 7);

	cout << &d1 << endl;
	cout << &d2 << endl;
	return 0;
}

這兩個(gè)運(yùn)算符一般不需要重載,使用編譯器生成的默認(rèn)取地址的重載即可,我們作為了解、知道有這兩個(gè)東西存在即可

只有特殊情況,才需要重載,比如想讓別人獲取到指定的內(nèi)容,就可以自己實(shí)現(xiàn)。

練習(xí):

根據(jù)前面所學(xué)知識的一個(gè)練習(xí):日期類的實(shí)現(xiàn)

要求:

1.能獲取某年某月的天數(shù)

2.寫出構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、析構(gòu)函數(shù)

3.判斷兩個(gè)日期的關(guān)系: > ; < ; <= ; >= ; !=?; ==

4.日期+天數(shù)、日期+=天數(shù)

5.日期-天數(shù)、日期-=天數(shù)

6.前置++、后置++,前置--,后置--

7.日期-日期,返回天數(shù)

代碼如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-438034.html

#pragma once
#include<iostream>
#include<string.h>
using namespace std;


//聲明:

class Date
{
public:
	void Print();

	//構(gòu)造函數(shù)
	Date(int year = 2023, int month = 1, int day = 1);

	//拷貝構(gòu)造函數(shù)
	Date(const Date& d);

	//析構(gòu)函數(shù)
	~Date();

	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+= (int day);
	Date operator+ (int day);
	Date& operator-= (int day);
	Date operator- (int day);

	// ++d
	Date& operator++();

	// d++
	Date operator++(int);

	// --d
	Date& operator--();

	// d--
	Date operator--(int);

	//日期-日期
	int operator-(const Date& d);

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







//定義:

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

int GetMonthDay(int year, int month)
{
	int monthArray[13] = { 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;
	}
	else
	{
		return monthArray[month];
	}
}

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

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

Date::~Date()
{
	_year = 2023;
	_month = 1;
	_day = 1;
}


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

bool Date::operator<(const Date& d)
{
	if (_year < d._year)
	{
		return true;
	}
	else if (_year == d._year && _month < d._month)
	{
		return true;
	}
	else if (_year == d._year && _month == d._month && _day < d._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}

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);
}

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


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;
}

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

Date& Date::operator-= (int day)
{
	if (day < 0)
	{
		*this += -day;
		return *this;
	}
	_day -= day;
	while (_day <= 0)
	{
		if (--_month == 0)
		{
			--_year;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

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


// ++d  顯示去調(diào):d1.operator++();
Date& Date::operator++()
{
	*this += 1;//調(diào)用上面寫的運(yùn)算符重載
	return *this;
}

// d++  后置++的類型是編譯器規(guī)定的這樣寫
//顯示去調(diào):d1.operator++(0);
Date Date::operator++(int)
{
	Date tmp(*this);

	*this += 1;

	return tmp;
}

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

// d--
Date Date::operator--(int)
{
	Date tmp(*this);

	*this -= 1;

	return tmp;
}

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

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

	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}
	return n * flag;
}




//測試
void text4()
{
	Date d1(2023, 5, 6);
	Date d2(2001, 3, 6);
	int life = d1 - d2;
	cout << life << endl;
}

int main()
{
	text4();



	return 0;
}

到了這里,關(guān)于C++好難(3):類和對象(中篇)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包