溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

什么是C++識類和對象

發(fā)布時(shí)間:2021-10-21 17:13:31 來源:億速云 閱讀:137 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“什么是C++識類和對象”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“什么是C++識類和對象”吧!

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

一個(gè)類中如果什么成員都沒有,那么這個(gè)類稱為空類。空類中是什么都沒有嗎?其實(shí)不然,任何一個(gè)類,再我們不寫的情況下,都會自動生成下面6個(gè)默認(rèn)成員函數(shù):

什么是C++識類和對象

本篇文章將對這幾個(gè)默認(rèn)成員函數(shù)進(jìn)行簡單介紹。

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

1.概念

我們先來看一下下面這個(gè)日期類:

class Date
{
public:
	void SetDate(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;
	d1.SetDate();
	d1.Print();
	return 0;
}

對于Date類,每次創(chuàng)建對象時(shí)可以調(diào)用SetData函數(shù)來設(shè)置對象的日期,但是如果每次創(chuàng)建對象時(shí)都需要調(diào)用該函數(shù)來設(shè)置日期信息,未免有些麻煩,那么能否再對象創(chuàng)建的同時(shí)就進(jìn)行初始化呢?

這里就需要用到類的默認(rèn)成員函數(shù)–構(gòu)造函數(shù)了。

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

2.特性

需要注意,構(gòu)造函數(shù)雖然名為構(gòu)造函數(shù),但是其作用并非為成員變量開辟空間,而是初始化對象。其特征如下:

函數(shù)名與類名相同。

沒有返回值。

編譯器會再對象實(shí)例化時(shí)自動調(diào)用構(gòu)造函數(shù)。

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

需要注意的是在類實(shí)例化對象的時(shí)候,如果變量后面帶上了(),而括號內(nèi)沒有參數(shù),那么這就成了函數(shù)聲明,該函數(shù)無參,且返回值為類名。

class Date
{
public:
	Date()//無參的構(gòu)造函數(shù)
	{
		_year = 0;
		_month = 1;
		_day = 1;
	}
	//帶參的構(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;//調(diào)用無參的構(gòu)造函數(shù)
	Date d2(0, 1, 1);//調(diào)用帶參的構(gòu)造函數(shù)
	Date d3();//無參,返回值為Date的函數(shù)聲明
	return 0;
}

隱式構(gòu)造函數(shù)

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

需要注意的是編譯器自己生成的構(gòu)造函數(shù)在初始化對象時(shí)做了一個(gè)偏心的處理:即對于內(nèi)置類型,編譯器不會處理;而對于自定義類型,編譯器會自定義類型調(diào)用它自己的默認(rèn)構(gòu)造函數(shù)。內(nèi)置類型指的是語法已經(jīng)定義好的類型,如:int,double,long等等;自定義類型是使用struct/class/union定義的類型。

這是什么意思呢?我們通過下面這個(gè)代碼來理解:

class C
{
public:
	C()
	{
		cout << "C()" << endl;
	}
private:
	int _c;
};
class Date
{
public:
	//若用戶顯式定義了構(gòu)造函數(shù),那么編譯器將不再生成
	/*Date()
	{
		_year = 0;
		_month = 1;
		_day = 1;
	}
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}*/
private:
	//內(nèi)置類型
	int _year;
	int _month;
	int _day;
	//自定義類型
	C c1;
};
int main()
{
	Date d1;//調(diào)用無參的構(gòu)造函數(shù)
	return 0;
}

什么是C++識類和對象

什么是C++識類和對象

通過調(diào)試可以發(fā)現(xiàn),d1自身的內(nèi)置類型變量仍為隨機(jī)值,編譯器調(diào)用的構(gòu)造函數(shù)并沒有處理,而對于自定義類型,可以看到編譯器調(diào)用了自定義類型中的默認(rèn)函數(shù),但是實(shí)際上如果調(diào)用編譯器自己生成的默認(rèn)構(gòu)造函數(shù),最終的結(jié)果就是所有的內(nèi)置類型變量仍然為隨機(jī)值,這么看下來好像編譯器自己生成的構(gòu)造函數(shù)好像沒什么用?

實(shí)則不然,比如我們曾做過用棧實(shí)現(xiàn)隊(duì)列的題,這道題的思路是用兩個(gè)棧來回倒保證隊(duì)列的先進(jìn)先出,而這里面的兩個(gè)結(jié)構(gòu)棧和用棧實(shí)現(xiàn)的隊(duì)列的代碼為:

class Stack//棧
{
public:
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
struct MyQueue//用兩個(gè)棧實(shí)現(xiàn)隊(duì)列
{
	Stack s1;
	Stack s2;
};

可以看到在用MyQueue這個(gè)類實(shí)例化對象時(shí),編譯器調(diào)用Stack中的構(gòu)造函數(shù)分別對成員變量s1和s2初始化,因此,我們無需再對其進(jìn)行初始化了,這相對來說方便了許多。

無參和全缺省的函數(shù)均為默認(rèn)構(gòu)造函數(shù)

無參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都被稱為默認(rèn)構(gòu)造函數(shù),但是需要注意的是:無參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)二者只能存在一個(gè),這是因?yàn)椋绻叨即嬖诘脑?,那么在?shí)例化對象不帶參數(shù)時(shí),編譯器無法區(qū)分是調(diào)用哪一個(gè)函數(shù)。

class Date
{
public:
	Date()
	{
		_year = 0;
		_month = 1;
		_day = 1;
	}
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;//錯誤,編譯器無法識別要調(diào)用哪一個(gè)構(gòu)造函數(shù)
	return 0;
}

在實(shí)際過程中,我們更傾向于使用全缺省的構(gòu)造函數(shù),因?yàn)樗藷o參的構(gòu)造函數(shù)的情況。

成員變量的命名風(fēng)格

可以注意到的是在定義類的時(shí)候成員變量前都加了一個(gè)_,這是為了防止下面這種情況:

class Date
{
public:
	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;
	d1.Print();
	return 0;
}

什么是C++識類和對象

可以看到,d1調(diào)用了構(gòu)造函數(shù)后,其成員變量認(rèn)為隨機(jī)值。這是因?yàn)樵趛ear = year這句代碼中,兩個(gè)year變量均為函數(shù)形參,實(shí)際上編譯器在處理這種變量時(shí),會遵循局部優(yōu)先原則,即編譯器在函數(shù)形參中找到了year變量,就不會繼續(xù)擴(kuò)大搜索范圍去尋找成員變量中的year變量,而在Print函數(shù)中,編譯器由于在形參中未找到y(tǒng)ear變量,因此繼續(xù)擴(kuò)大搜索范圍,在成員變量中找到了year并使用之。

因此,在聲明成員變量的命名時(shí)需要遵循一定的規(guī)范,常見的有:(1)在變量名前加_,如_year (2)在變量名后加_,如year_ (3)駝峰法,如mYear,m表示member。

另外,上述情況可以通過使用this指針進(jìn)行解決,即將代碼改為this->year = year;但在實(shí)際使用過程中,最好還是注重成員變量的命名

補(bǔ)充

由于早期c++語法設(shè)計(jì)的缺陷,編譯器默認(rèn)生成的構(gòu)造函數(shù)并不會對內(nèi)置類型變量初始化,因此在c++11后,語法委員會在成員變量聲明處打了一個(gè)補(bǔ)丁,運(yùn)行,變量聲明的同時(shí)加上缺省值,比如:

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	//注意,此處僅為缺省值,仍為變量聲明,而非初始化(定義)
	int _year = 0;
	int _month = 1;
	int _day = 1;
};

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

1.概念

與構(gòu)造函數(shù)相比,析構(gòu)函數(shù)相對簡單一些。析構(gòu)函數(shù)的作用與構(gòu)造函數(shù)的相反,析構(gòu)函數(shù)并不是完成對象的銷毀,因?yàn)榫植繉ο蟮匿N毀工作是由編譯器來完成的。一個(gè)詞來概括析構(gòu)函數(shù)的作用就是清理,即對象在銷毀的時(shí)候會自動調(diào)用析構(gòu)函數(shù),完成類當(dāng)中的一些資源清理工作。

2.特性

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

析構(gòu)函數(shù)名是類名前加上~號

析構(gòu)函數(shù)無參數(shù)無返回值

一個(gè)類有且只有一個(gè)析構(gòu)函數(shù)

若析構(gòu)函數(shù)為顯式定義,那么系統(tǒng)會自動生成默認(rèn)的析構(gòu)函數(shù)。

與構(gòu)造函數(shù)一樣,系統(tǒng)的默認(rèn)析構(gòu)函數(shù)對于內(nèi)置類型變量不會處理,對于自定義變量會調(diào)用其自身的析構(gòu)函數(shù)。

其次,對于Date類這樣的類,由于其內(nèi)部沒有什么資源需要處理,因此不需要析構(gòu)函數(shù);對于Stack這樣的類,其內(nèi)部由資源需要處理,比如對malloc出來的空間進(jìn)行釋放,因此需要實(shí)現(xiàn)析構(gòu)函數(shù)。

還是之前的代碼,在用兩個(gè)棧實(shí)現(xiàn)隊(duì)列中,在Stack類中實(shí)現(xiàn)了構(gòu)造函數(shù)和析構(gòu)函數(shù),那么用MyQueue實(shí)例化my變量后無法自己實(shí)現(xiàn)初始化和空間的釋放:

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		free(_a);
		_a = NULL;
		_top = _capacity = 0;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
struct MyQueue
{
	Stack s1;
	Stack s2;
};
int main()
{
    //我們無需自己對mq進(jìn)行初始化和清理空間
    //編譯會自動調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)
	MyQueue mq;
	return 0;
}
c++編譯器在對象生命周期結(jié)束時(shí)自動調(diào)用析構(gòu)函數(shù)
class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	return 0;//編譯器在執(zhí)行這句代碼的同時(shí)會調(diào)用類中的析構(gòu)函數(shù)
}

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

1.概念

拷貝構(gòu)造函數(shù),顧名思義,其作用就是創(chuàng)建一個(gè)和被拷貝對象一模一樣的對象。

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

2.特性

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

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

參數(shù)只有一個(gè)且為引用傳參

拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須為引用傳參,使用傳值方式會引發(fā)無窮遞歸調(diào)用。

class Date
{
public:
	Date()
	{
		_year = 0;
		_month = 1;
		_day = 1;
	}
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

那么為什么說傳值會導(dǎo)致無窮遞歸調(diào)用呢?首先我們需要理解到調(diào)用函數(shù)傳值給形參也是一種拷貝,比如說:

什么是C++識類和對象

同樣的,對于拷貝構(gòu)造函數(shù),若形參為傳值調(diào)用,那么在上述代碼中將d2賦值給形參d時(shí)也會調(diào)用拷貝構(gòu)造函數(shù),而每一次調(diào)用拷貝構(gòu)造函數(shù)都會經(jīng)過依次賦值操作,從而導(dǎo)致無窮遞歸調(diào)用:

什么是C++識類和對象

而傳引用就能夠很好的解決這個(gè)問題,其次,傳指針也可以達(dá)到目的,不過一般傳引用的話可以增強(qiáng)代碼可讀性。

若未顯式定義,系統(tǒng)會生成默認(rèn)的拷貝構(gòu)造函數(shù)

與構(gòu)造函數(shù)一樣,如果我們自己沒有實(shí)現(xiàn)拷貝構(gòu)造函數(shù),那么編譯器會生成默認(rèn)的拷貝構(gòu)造函數(shù);但是與構(gòu)造函數(shù)不同的是,默認(rèn)的拷貝構(gòu)造函數(shù)對于內(nèi)置類型和自定義類型變量都會處理:

(1)對于內(nèi)置類型,默認(rèn)的拷貝構(gòu)造函數(shù)會對對象進(jìn)行淺拷貝,即按照內(nèi)存存儲中的字節(jié)序?qū)ο筮M(jìn)行拷貝,也叫值拷貝。

(2)對于自定義類型,默認(rèn)的拷貝構(gòu)造函數(shù)會調(diào)用自定義類型中自己的拷貝構(gòu)造函數(shù)。

class A
{
public:
	A()
	{
		_a = 0;
	}
	A(const A& a)
	{
		cout << "A(const A& a)" << endl;
	}
private:
	int _a;
};
class Date
{
public:
	Date()
	{
		_year = 0;
		_month = 1;
		_day = 1;
	}
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//調(diào)用默認(rèn)的拷貝構(gòu)造函數(shù)
	/*Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}*/
private:
	int _year;
	int _month;
	int _day;
	A aa;
};
int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

什么是C++識類和對象

淺拷貝的注意事項(xiàng)

通過上面我們知道了默認(rèn)的拷貝構(gòu)造函數(shù)能夠?qū)崿F(xiàn)淺拷貝,也就是說,對于Date這樣的類,我們無需自己實(shí)現(xiàn)拷貝構(gòu)造函數(shù)只用默認(rèn)的拷貝構(gòu)造函數(shù)就能夠?qū)崿F(xiàn)拷貝目的,那么是否用編譯器自己的函數(shù)就夠了呢?

其實(shí)不然,比如我們熟知的Stack類,如果直接調(diào)用系統(tǒng)默認(rèn)的拷貝構(gòu)造函數(shù):

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

上述代碼,我們運(yùn)行后發(fā)現(xiàn),程序崩潰了,這是為什么呢?這是因?yàn)橄到y(tǒng)默認(rèn)的拷貝構(gòu)造函數(shù)拷貝出了一份與s1一模一樣的s2:

什么是C++識類和對象

而我們知道當(dāng)對象的生命周期結(jié)束時(shí),系統(tǒng)會自動調(diào)用析構(gòu)函數(shù)對類空間進(jìn)行清理,由于s2是后壓棧的,因此會先清理,這時(shí)s2._a所指的空間已經(jīng)free還給操作系統(tǒng)了,但是s1還會再次調(diào)用析構(gòu)函數(shù),將已經(jīng)釋放的s1._a所指向的空間再一次釋放(注意,s2._a釋放完后s1._a仍指向原空間,此時(shí)s1._a為野指針),這個(gè)操作最終會導(dǎo)致程序崩潰。

什么是C++識類和對象

可見編譯器默認(rèn)的拷貝構(gòu)造函數(shù)并不能解決所有的問題,淺拷貝會導(dǎo)致一些錯誤,那么要如何解決淺拷貝的帶來的問題呢?這就要我們之后介紹的深拷貝來解決了。

感謝各位的閱讀,以上就是“什么是C++識類和對象”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對什么是C++識類和對象這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

c++
AI