?
一.再談構(gòu)造函數(shù)
構(gòu)造函數(shù)其實(shí)分為:
? ?1.函數(shù)體賦值
? ?2.初始化列表
之前所講到的構(gòu)造函數(shù)其實(shí)都是函數(shù)體賦值,那么本篇文章將會具體講述初始化列表。
初始化列表
語法
以一個(gè)冒號開始,接著是一個(gè)以逗號分隔的數(shù)據(jù)成員列表,每個(gè)"成員變量"后面跟
一個(gè)放在括號中的初始值或表達(dá)式。class Date { public: Date(int year, int month, int day) ??: _year(year) ??, _month(month) ??, _day(day) {} private: int _year; int _month; int _day; };
上述代碼即是初始化列表。
必須用初始化列表初始化的變量
需要注意的是,有幾種變量必須要用初始化列表初始化:
? ?1.const 變量
? ?2.引用變量
? ?3.自定義變量
接下來我們一個(gè)一看。
const 變量
可以看到在函數(shù)體中對 const 變量是不可以初始化的,所以必須要在初始化列表中初始化;
引用變量
?很明顯,對于引用變量也不能在函數(shù)體中初始化;這里還要注意給引用傳參時(shí),也要傳引用,否則會出現(xiàn)類似野引用的情況,這種情況很危險(xiǎn)。
自定義變量
對于自自定義變量,會去調(diào)用它的默認(rèn)構(gòu)造函數(shù),所以不顯式初始化自定義變量也行,但如果該自定義變量沒有默認(rèn)構(gòu)造函數(shù)的話,就必須要顯式初始化(關(guān)于默認(rèn)構(gòu)造函數(shù):構(gòu)造函數(shù)和析構(gòu)函數(shù))
如上圖所示,對于沒有默認(rèn)構(gòu)造函數(shù)的自定義變量,因?yàn)槲达@示初始化,所以編譯器報(bào)了錯。?
初始化列表的一個(gè)坑
我們先來看一段代碼:
class A
{
public:
??A(int a)
???:_a1(a)
???,_a2(_a1)
?{}
?
??void Print()
{
????cout<<_a1<<" "<<_a2<<endl;
?}
private:
??int _a2;
??int _a1;
};
int main()
{
??A aa(1);
??aa.Print();
}
上面這段代碼會輸出什么呢?
答案是:1? ?隨機(jī)值
為什么?
這就不得不說到初始化列表的一個(gè)有點(diǎn)坑的地方了。
成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后
次序無關(guān);也就是說,上述代碼的初始化列表中,先初始化的是 _a2 變量,而 _a2 變量是初始化成 _a1 變量的,但此時(shí) _a1 變量還沒有初始化,所以就出現(xiàn)了隨機(jī)值。
所以呢,初始化列表時(shí)最好按照聲明的順序初始化。
總結(jié)
1.初始化列表其實(shí)是成員變量定義的地方,不管有沒有寫都會走一遍,且也只會走一遍;
? private? 中的其實(shí)是成員變量的聲明;
2.盡量使用初始化列表初始化,因?yàn)椴还苣闶欠袷褂贸跏蓟斜恚瑢τ谧远x類型成員變? ? ? ? 量,一定會先使用初始化列表初始化。
3.初始化列表并不能完全代替函數(shù)體賦值。
二.explicit 關(guān)鍵字
內(nèi)置類型與自定義類型的隱式類型轉(zhuǎn)換
先看這樣一段1代碼
class A
{
public:
A(int a=1)
:_a(a)
{}
private:
int _a;
};
int main()
{
A a1(1);
A a2 = 2; //這句代碼有問題嗎
return 0;
}
我們發(fā)現(xiàn)了一個(gè)令人有點(diǎn)摸不著頭腦的代碼: A? a2=2 ;
這是什么?
其實(shí)這就是隱式類型轉(zhuǎn)換;
內(nèi)置類型先轉(zhuǎn)換成自定義類型,然后構(gòu)造一個(gè)A的臨時(shí)對象(臨時(shí)對象具有常屬性),臨時(shí)對象再拷貝構(gòu)造a2 ,最后再調(diào)用構(gòu)造函數(shù),但是現(xiàn)在的編譯器一般都會對這一過程進(jìn)行優(yōu)化,它是直接構(gòu)造。
我們可以驗(yàn)證下:
?可以看到 vs2022 是進(jìn)行了優(yōu)化的,直接調(diào)用構(gòu)造函數(shù)。
那么如果我們不想讓這種隱式類型轉(zhuǎn)換發(fā)生該怎么辦呢?
?只需再函數(shù)前面加上 explicit 關(guān)鍵字即可解決explicit?
?可以看到在加上這個(gè)關(guān)鍵字后,編譯器就報(bào)錯了。
三.static 成員
靜態(tài)成員變量
1.聲明為static的類成員稱為類的靜態(tài)成員,用static修飾的成員變量,稱之為靜態(tài)成員變量;
? ?注意:靜態(tài)成員變量一定要在類外進(jìn)行初始化,且不加 static 關(guān)鍵字
? ? ? ? ? ? ? 類靜態(tài)成員即可用 類名::靜態(tài)成員 或者 對象.靜態(tài)成員 來訪問;
class A { public: A(int b=1) :_b(b) {} private: static int _a; //聲明 int _b; }; int A::_a = 1; //定義
靜態(tài)成員函數(shù)
2.用static修飾的成員函數(shù),稱之為靜態(tài)成員函數(shù)
? ?注意:靜態(tài)成員函數(shù)沒有this指針,所以不能訪問類里面的成員,也不能調(diào)用非靜態(tài)成員? ? ? ? ? ? ? ? ? 函數(shù);
static 成員屬于類,屬于類的每個(gè)對象共享,存儲在靜態(tài)區(qū);
成員變量 -- 屬于每個(gè)一個(gè)類對象,存儲在對象里面;
靜態(tài)成員也是類的成員,受public、protected、private 訪問限定符的限制。
四.友元
友元函數(shù)
友元函數(shù)可以直接訪問類的私有成員,它是定義在類外部的普通函數(shù),不屬于任何類,但需要在類的內(nèi)部聲明(友元函數(shù)并不受訪問限定符限制),聲明時(shí)需要加friend關(guān)鍵字。
例:重載運(yùn)算符? <<
我們知道 cout<< 能自動識別內(nèi)置類型,并打印;如果想要用這個(gè)打印自定義類型的話,就要重載一個(gè),如果重載在類中的話,那么它就屬于類的成員函數(shù)了,第一個(gè)形參就是 this指針,所以我們使用的時(shí)候只能這樣寫:對象 << cout,這樣是不是很別扭,所以要想按照原來的寫法,就不能把這個(gè)函數(shù)寫在類的內(nèi)部,只能寫在外部,但我們有序要訪問類里面的成員,這就在類內(nèi)部聲明友元函數(shù)了。
class Eve
{
friend ostream& operator<<(ostream& out, Eve& e); //友元函數(shù)聲明
public:
Eve(int a,int b)
:_a(a)
,_b(b)
{}
private:
int _a = 1;
int _b = 2;
};
ostream& operator<<(ostream& out, Eve& e)
{
out << e._a <<" "<<e._b << endl;
return out;
}
int main()
{
Eve e(1,2);
cout << e;
return 0;
}
總結(jié)
1.友元函數(shù)可訪問類的私有和保護(hù)成員,但不是類的成員函數(shù);
2.友元函數(shù)不能用const修飾;
3.友元函數(shù)可以在類定義的任何地方聲明,不受類訪問限定符限制;
4.一個(gè)函數(shù)可以是多個(gè)類的友元函數(shù);
5.友元函數(shù)的調(diào)用與普通函數(shù)的調(diào)用原理相同;
友元類
友元類的所有成員函數(shù)都可以是另一個(gè)類的友元函數(shù),都可以訪問另一個(gè)類中的非公有成員。
1.友元關(guān)系是單向的,不具有交換性;
? ?比如A類和B類,在A類中聲明B類為其友元類,那么可以在A類中直接訪問B類的私有成員? ? ?變量,但想在B類中訪問A類中私有的成員變量則不行。
2.友元關(guān)系不能傳遞;
3.如果C是B的友元, B是A的友元,則不能說明C時(shí)A的友元;
4.友元關(guān)系不能繼承,在繼承位置再給大家詳細(xì)介紹。
五.內(nèi)部類
概念:如果一個(gè)類定義在另一個(gè)類的內(nèi)部,這個(gè)內(nèi)部類就叫做內(nèi)部類。
內(nèi)部類是一個(gè)獨(dú)立的類,它不屬于外部類,更不能通過外部類的對象去訪問內(nèi)部類的成員。外部類對內(nèi)部類沒有任何優(yōu)越的訪問權(quán)限。
所以計(jì)算一個(gè)有內(nèi)部類的類的大小時(shí),只需要計(jì)算外部類的大小。
注意:內(nèi)部類天生是外部類的友元類。
特性:
1. 內(nèi)部類可以定義在外部類的public、protected、private都是可以的。
2. 注意內(nèi)部類可以直接訪問外部類中的static成員,不需要外部類的對象/類名。
3. sizeof(外部類)=外部類,和內(nèi)部類沒有任何關(guān)系。
class A
{
private:
static int _a;
int _b;
class B
{
private:
int _c;
};
};
int main()
{
cout << sizeof(A) << endl; //會是多少呢?
return 0;
}
?上述代碼的結(jié)果是什么呢?
答案:4
因?yàn)橹恍枰?jì)算外部類的大小,而靜態(tài)成員變量是存儲在靜態(tài)區(qū)的,并不在類中,所以只需計(jì)算成員變量 _b 的大小,很明顯是4。
六.匿名對象
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
class Solution
{
public:
int Sum_Solution(int n)
{
//...
return n;
}
};
int main()
{
A aa1;
// 不能這么定義對象,因?yàn)榫幾g器無法識別下面是一個(gè)函數(shù)聲明,還是對象定義
//A aa1();
// 但是我們可以這么定義匿名對象,匿名對象的特點(diǎn)不用取名字,
// 但是他的生命周期只有這一行,我們可以看到下一行他就會自動調(diào)用析構(gòu)函數(shù)
A();
A aa2(2);
// 匿名對象在這樣場景下就很好用
Solution().Sum_Solution(10);
return 0;
}
????本篇文章到此就結(jié)束了,?若有錯誤或是建議的話,歡迎小伙伴們指出;?????
????希望小伙伴們能支持支持博主啊,你們的支持對我很重要哦;????
????謝謝你的閱讀。????文章來源:http://www.zghlxwxcb.cn/news/detail-446234.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-446234.html
到了這里,關(guān)于【C++初階】類和對象(下)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!