??個人主頁:?? :???初階牛???
??推薦專欄1: ??????C語言初階
??推薦專欄2: ??????C語言進(jìn)階
??個人信條: ??知行合一
??本篇簡介:>:深入理解構(gòu)造函數(shù),介紹友元函數(shù),內(nèi)部類等等
金句分享:
?努力不一定是為了錢,還有骨子里的自信與淡定?
一、構(gòu)造函數(shù)的深入理解
1.1 初始化列表
前面,我們已經(jīng)學(xué)習(xí)過構(gòu)造函數(shù),在創(chuàng)建對象的時候,編譯器會自動調(diào)用構(gòu)造函數(shù),用于給初始化對對象的成員變量賦予初始值.那構(gòu)造函數(shù)體內(nèi)的語句時初始化嗎?
class Date
{
public:
Date(int year, int month, int day)
{
//下面這些是對成員變量的初始化操作嗎?
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
答案:
并不是初始化操作,因?yàn)槌跏蓟荒艹跏蓟淮?是指變量在創(chuàng)建的時候被賦予的初始值.而構(gòu)造函數(shù)體內(nèi)可以進(jìn)行多次賦值.
那成員變量是在哪里初始化的呢?
運(yùn)行結(jié)果:
2023-2-1
類的成員變量會先經(jīng)過初始化列表,再走函數(shù)體內(nèi)賦值,所以month
初始化為了1
,后又在函數(shù)體內(nèi)被重新賦值.為了2
.
在構(gòu)造函數(shù)的函數(shù)名參數(shù)后與{}中間的區(qū)域是成員變量初始化的地方.
初始化列表:
以一個冒號開始,接著是一個以逗號分隔的數(shù)據(jù)成員列表,每個"成員變量"后面跟一個放在括號中的初始值或表達(dá)式。(如上圖)
(1) 初始化列表的作用:
我們未使用初始化列表之前,一直都是在函數(shù)體內(nèi)賦值,那初始化列表有什么用呢?
試著看一下下面這段代碼.
對于下列成員變量,只能使用初始化列表進(jìn)行初始化,因?yàn)檫@些成員變量只能在定義時就給出初始化的值:
-
const
成員變量 - 引用成員變量
- 沒有默認(rèn)構(gòu)造函數(shù)的自定義類型成員
正確寫法:
class Date
{
public:
Date(int year = 2023, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
, pa(day) //在初始化列表對這些特殊的成員變量初始化
, b(2)
,t1(6,15,20)
{
_month = 2;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
int& pa;
const int b;
Time t1;
};
(2) 初始化列表的初始化順序與成員變量的聲明有關(guān),與寫在初始化列表的順序無關(guān).
示例;
并不會先個c
賦值,而是按a,b,c
的順序進(jìn)行初始化,此時a
是使用未初始化的b
進(jìn)行初始化,b
是使用未初始化的c
來初始化,最后c
使用66
初始化.
故結(jié)果a
和b
都是隨機(jī)值.
1.2 關(guān)鍵字:explicit
構(gòu)造函數(shù)不僅可以構(gòu)造與初始化對象,對于以下三種構(gòu)造函數(shù),還具有類型轉(zhuǎn)換的作用。
- 單個參數(shù)構(gòu)造函數(shù)
示例:Test(int a )
- 除第一個參數(shù)無默認(rèn)值其余均有默認(rèn)值的構(gòu)造函數(shù).
示例:Test(int a, int b = 66, int c = 88)
- 全缺省的構(gòu)造函數(shù).
示例:Test(int a=20, int b = 66, int c = 88)
類型轉(zhuǎn)換的情況展示:
令t1=num
,num
將會被賦值給第一個參數(shù).
使用 explicit
后,編譯器會報錯.
在C++
中,關(guān)鍵字explicit
用來修飾類的構(gòu)造函數(shù),它的作用是防止隱式類型轉(zhuǎn)換。當(dāng)一個類的構(gòu)造函數(shù)被聲明為explicit
時,編譯器將不會自動執(zhí)行隱式類型轉(zhuǎn)換,而只能進(jìn)行顯式類型轉(zhuǎn)換。這樣會提高代碼的可讀性,隱式類型轉(zhuǎn)換可讀性不好.
顯示類型轉(zhuǎn)換:↓
附上對應(yīng)代碼:
class Test
{
public:
//1. 單參數(shù)構(gòu)造
//Test(int a )
//{
// _a = a;
//}
//2. 除第一個參數(shù)無默認(rèn)值其余均有默認(rèn)值的構(gòu)造函數(shù)
//Test(int a, int b = 66, int c = 88)
// : _a(a)
// , _b(b)
// ,_c(c)
//{
//}
//3. 全缺省構(gòu)造
//Test(int a = 20, int b = 66, int c = 88)
// : _a(a)
// , _b(b)
// , _c(c)
//{
//}
explicit Test(int a=20, int b = 66, int c = 88)
: _a(a)
, _b(b)
,_c(c)
{
}
void Print()
{
cout << _a << endl;
cout << _b << endl;
cout << _c << endl;
}
private:
int _a;
int _b;
int _c;
};
void test1()
{
Test t1;
t1.Print();
cout << endl;
int num = 99;
t1 =(Test) num;
t1.Print();
}
int main()
{
test1();
return 0;
}
二、Static成員變量/函數(shù)
(1)定義
靜態(tài)成員變量和靜態(tài)成員函數(shù)是屬于類而不是對象的成員。它們與類的實(shí)例對象無關(guān),而是與整個類相關(guān)聯(lián)。
靜態(tài)成員變量(static member variable)是在類中使用關(guān)鍵字static
聲明的成員變量。它不屬于類的任何特定實(shí)例對象,而是屬于整個類。只會有一個靜態(tài)成員變量的副本被共享給所有的類的實(shí)例對象。可以直接通過類名訪問靜態(tài)成員變量,也可以通過類的對象進(jìn)行訪問。
靜態(tài)成員函數(shù)(static member function)是通過關(guān)鍵字static
聲明的類成員函數(shù)。與普通成員函數(shù)不同,靜態(tài)成員函數(shù)不依賴于類的實(shí)例對象。它只能訪問類的靜態(tài)成員,不能訪問非靜態(tài)成員。靜態(tài)成員函數(shù)可以直接通過類名進(jìn)行調(diào)用,而不需要創(chuàng)建類的實(shí)例對象。
(2)靜態(tài)成員函數(shù)為什么一定要在類外面初始化:
C++
中的靜態(tài)成員變量在程序運(yùn)行時被分配內(nèi)存,但是它們的定義是在編譯時就已經(jīng)完成的。在聲明靜態(tài)成員變量時,需要在類的定義中進(jìn)行,但不能在函數(shù)內(nèi)部進(jìn)行。在類的定義中聲明的靜態(tài)成員變量不會占用內(nèi)存空間,只有在類外定義時才會真正地分配內(nèi)存。
因此,當(dāng)在程序中第一次使用靜態(tài)成員變量時,它們才會被真正地生成并分配內(nèi)存。這通常是在程序的main函數(shù)執(zhí)行之前發(fā)生的。如果程序中沒有使用靜態(tài)成員變量,它們可能永遠(yuǎn)不會被生成。
-
存儲空間分配:
靜態(tài)成員變量
需要在內(nèi)存中分配存儲空間,類的定義只是描述了該成員變量的類型和訪問方式,只是圖紙,并沒有分配空間。所以在類外進(jìn)行初始化方便為其分配存儲空間。 -
只能初始化一次:
靜態(tài)成員變量
屬于整個類,不屬于某個對象,靜態(tài)成員變量
在整個類的生命周期中只能被初始化一次。如果在類的定義中進(jìn)行初始化,那么每個包含該類定義的文件都會進(jìn)行初始化,這違背了靜態(tài)成員變量只應(yīng)初始化一次的原則。將初始化操作移到類外,可以確保只有一次初始化。 -
存儲空間的鏈接性:將靜態(tài)成員變量的初始化放在類外,可以保持存儲空間的鏈接性。如果多個不同的源文件都包含了該類的定義并進(jìn)行了初始化,鏈接器無法確定使用哪個初始化值,從而導(dǎo)致鏈接錯誤。將初始化放在類的實(shí)現(xiàn)文件中,可以避免鏈接錯誤。
總結(jié):
靜態(tài)成員變量
和靜態(tài)成員函數(shù)
特點(diǎn)如下:
-
靜態(tài)成員為所有類對象所共享,不屬于某個具體的對象,存放在靜態(tài)區(qū).
-
靜態(tài)成員變量必須在類外定義,類中只是聲明,定義時指定類域,并且不需要
static
關(guān)鍵字. -
訪問方式(前提是公有,如果是私有,需要在類中定義一個函數(shù)去返回):
(1)類名::靜態(tài)成員
(2)對象.靜態(tài)成員 (不推薦) -
靜態(tài)成員函數(shù)不屬于某個對象,所以沒有隱藏的
this
指針,不能訪問任何非靜態(tài)成員. -
靜態(tài)成員也是類的成員,受
public
、protected
、private
訪問限定符的限制
靜態(tài)成員變量和靜態(tài)成員函數(shù)的主要用途包括:
- 對象計(jì)數(shù)器:可以使用靜態(tài)成員變量來實(shí)現(xiàn)某個類的對象的計(jì)數(shù)功能。
class Test
{
public:
Test()//構(gòu)造函數(shù)
{
++_count;
}
Test(Test& t)//拷貝構(gòu)造
{
++_count;
}
~Test()
{
--_count;
}
static int GetCount()
{
return _count;
}
private:
static int _count;
};
int Test::_count = 0;
void test1()
{
cout << Test::GetCount() << endl;
Test t1,t2;
Test t3(t1);
Test t4;
cout << Test::GetCount() << endl;
}
- 共享數(shù)據(jù):靜態(tài)成員變量可以用于在類的所有實(shí)例對象之間共享某些數(shù)據(jù)。
- 工具函數(shù):靜態(tài)成員函數(shù)可以作為工具函數(shù),獨(dú)立于對象的操作,提供一些輔助功能。
靜態(tài)成員變量
和靜態(tài)成員函數(shù)
為類提供了與類相關(guān)的特性和功能,并且可以在不創(chuàng)建類的實(shí)例對象的情況下進(jìn)行訪問和使用。
- 靜態(tài)成員函數(shù)可以調(diào)用非靜態(tài)成員函數(shù)嗎?
不可以,靜態(tài)成員函數(shù)不能直接調(diào)用非靜態(tài)成員函數(shù)。因?yàn)殪o態(tài)成員函數(shù)是屬于類的,而非靜態(tài)成員函數(shù)是屬于對象的。靜態(tài)成員函數(shù)沒有指向具體對象的指針,因此不能訪問對象的非靜態(tài)成員函數(shù)和非靜態(tài)成員變量。如果需要在靜態(tài)成員函數(shù)中調(diào)用非靜態(tài)成員函數(shù),可以先創(chuàng)建一個對象,然后通過對象調(diào)用非靜態(tài)成員函數(shù)。
- 非靜態(tài)成員函數(shù)可以調(diào)用類的靜態(tài)成員函數(shù)嗎?
可以,非靜態(tài)成員函數(shù)可以調(diào)用類的靜態(tài)成員函數(shù)。靜態(tài)成員函數(shù)是與類相關(guān)聯(lián)的函數(shù),而不是與類的任何特定對象相關(guān)聯(lián)的函數(shù)。因此,非靜態(tài)成員函數(shù)可以使用類的靜態(tài)成員函數(shù),因?yàn)殪o態(tài)成員函數(shù)不依賴于特定對象的存在。
三、 友元
(1) 友元函數(shù)
當(dāng)我們需要實(shí)現(xiàn)流運(yùn)算符重載
時,會出現(xiàn)一個比較尷尬的問題,那就是第一個參數(shù)被this
指針占據(jù),且無法改變,這就造成左操作數(shù)是對象,調(diào)用起來十分別扭.
示例:
class Date
{
public:
Date(int year = 2023, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
_month = 2;
}
//第一個參數(shù)被this指針占據(jù)了,所以ostream& _cout只能作為右操作數(shù),則調(diào)用起來就很別扭.
ostream& operator<<(ostream& _cout)
{
_cout << _year << "-" << _month << "-" << _day << endl;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
void test1()
{
Date d1;
d1 << cout;//別扭的調(diào)用
}
int main()
{
test1();
return 0;
}
由于類的成員函數(shù)第一個參數(shù)被this
指針占據(jù),所以流運(yùn)算符重載只能寫成全局函數(shù),但是全局函數(shù)無法訪問類中的私有成員,為了能夠在類的外面也可以訪問類中的私有成員.
友元函數(shù)的出現(xiàn),以朋友的身份,去家(類)里參觀.
class Date
{
public:
friend ostream& operator<<(ostream& _cout, const Date& d);//友元函數(shù)只是一個聲明,不受public,private等訪問限定符影響,是在類外面的定義的.
Date(int year = 2023, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
_month = 2;
}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)//這是類外面的函數(shù),沒有this指針
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
void test1()
{
Date d1;
cout<<d1<<endl;//順眼的調(diào)用
}
這么說吧.友元函數(shù)是類的關(guān)系戶,類外面別的函數(shù)都受類域的限制,不能訪問類中的私有成員和保護(hù)成員,但是友元函數(shù)卻可以,一個特殊的存在,由于這樣操作破壞了類的封裝性,我們建議少使用友元.
小結(jié):
- 友元函數(shù)可訪問類的私有(
private
)和保護(hù)(protect
)成員,但友元函數(shù)不屬于類,不是類的成員函數(shù). - 友元函數(shù)不能用
const
修飾 - 因?yàn)橛言瘮?shù)不屬于類,所以不受
public
,private
等訪問限定符影響,只是一個聲明,在類中的哪出現(xiàn)都可以. - 友元函數(shù)的調(diào)用與普通函數(shù)的調(diào)用原理相同
(2)友元類
前面介紹了友元函數(shù),那類也可以是類的友元.
- 友元類的所有成員函數(shù)都可以是另一個類的友元函數(shù),都可以訪問另一個類中的非公有成員。
- 但是友元關(guān)系是單向的,不具有交換性。
示例:如果Date
類是Time
類的友元,即在Time
類中聲明,Date
是他的朋友.
則可以在Date
類中直接訪問Time
類的私有成員變量,但是在Time
類中是無法訪問Date
類中的私有成員的. - 友元關(guān)系不能傳遞.
如果B是A的友元,C是B的友元,則不能說明C時A的友元.就比如.
你朋友的朋友不一定是你的朋友.
class Time
{
public:
friend class Date;//友元類只是一個聲明,不受public,private等訪問限定符影響,是在類外面的定義的.
Time(int hour=6, int minute=30, int second=30)
{
_hour = hour;
_minute = minute;
_second = second;
}
void Test()
{
cout << d1._year;//報錯,無法訪問,因?yàn)镈ate類并沒有聲明Time是自己的友元類
}
private:
int _hour;
int _minute;
int _second;
Date d1;
};
class Date
{
public:
Date(int year = 2023, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
//可以訪問,因?yàn)門ime類聲明了Date是它的友元類
cout << t1._hour << "-" << t1._minute << "-" << t1._second << endl;
}
private:
int _year;
int _month;
int _day;
Time t1;
};
void test1()
{
Date d1;
d1.Print();
}
四、內(nèi)部類(天生友元)
如果一個類A
它定義在另外一個類B的里面(內(nèi)部),則類A
是類B
的內(nèi)部類.
外部類對內(nèi)部類沒有任何特權(quán),但是內(nèi)部類卻是外部類的天生友元.
class Date//外部類
{
public:
Date(int year = 2023, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
}
private:
int _year;
int _month;
int _day;
static int a;
public:
class Time//內(nèi)部類
{
public:
void Test(const Date& d1)
{
cout << d1._year << "-" << d1._month << "-" << d1._day << endl;//是外部類的天生友元,可以訪問外部類的私有成員
a = 5;//可以直接訪問外部類的靜態(tài)成員變量
}
};
};
int Date::a = 3;
內(nèi)部類的特點(diǎn):
- 內(nèi)部類可以定義在外部類的
public
、protected
、private
中皆可,訪問時受域作用限定符的限制. - 外部類并不是包括內(nèi)部類,即sizeof(外部類)=外部類,內(nèi)部類只是在外部類的類域中定義,并不占空間.
- 內(nèi)部類可以直接訪問外部類中的
static
成員,不需要外部類的對象/類名。
C++
中內(nèi)部類用的并不多.文章來源:http://www.zghlxwxcb.cn/news/detail-636505.html
本篇到此結(jié)束,覺得不錯的小伙伴可以三連支持一下.謝謝.文章來源地址http://www.zghlxwxcb.cn/news/detail-636505.html
到了這里,關(guān)于那些你不知道的類和對象的知識的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!