ps6做網(wǎng)站點(diǎn)哪里保存seo最新
W...Y的主頁(yè)😊
代碼倉(cāng)庫(kù)分享💕?
🍔前言:
關(guān)于C++的博客中,我們已經(jīng)了解了六個(gè)默認(rèn)函數(shù)中的四個(gè),分別是構(gòu)造函數(shù)、析構(gòu)函數(shù)、拷貝構(gòu)造函數(shù)以及函數(shù)的重載。但是這些函數(shù)都是有返回值與參數(shù)的。提到參數(shù)與返回值我們就會(huì)想到可以修飾它們的一個(gè)關(guān)鍵字const。而且關(guān)于構(gòu)造函數(shù),我們并沒有將內(nèi)容全部講完,所以我們今天這篇博客就是對(duì)const關(guān)鍵字的講解以及構(gòu)造函數(shù)的補(bǔ)充!話不多說,我們直接開始。
目錄
const成員
?取地址及const取地址操作符重載
再談構(gòu)造函數(shù)
初始化列表
const成員
const對(duì)于我們有語(yǔ)言基礎(chǔ)的人并不陌生,就是關(guān)于修飾變量使其成為一個(gè)不可修改的內(nèi)容。在C++中也是如此,但是C++中類的出現(xiàn),伴隨的出現(xiàn)的就是一系列的成員函數(shù),而被const修飾的成員函數(shù)就是const成員函數(shù)。
我們來看一下這段代碼:
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << "Print()" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl << endl;
}private:
int _year; // 年
int _month; // 月
int _day; // 日
};
void Test()
{
Date d1(2022,1,13);
d1.Print();
const Date d2(2022,1,13);
d2.Print();
}
上述代碼是有錯(cuò)的,在編譯器編譯時(shí)就會(huì)出現(xiàn)
這是為什么呢?當(dāng)我們使用const修飾d2時(shí),d2的類型就是const Date類型,而我們?nèi)フ{(diào)用print函數(shù)去打印時(shí),print隱藏的函數(shù)參數(shù)其實(shí)是Date* const this,所以參數(shù)不匹配導(dǎo)致程序報(bào)錯(cuò)。
那this指針的參數(shù)應(yīng)該怎么是隱藏的,所以C++規(guī)定在函數(shù)后加上const的實(shí)際意義就是在this指針前加const。
所以正確的print函數(shù)應(yīng)該在函數(shù)后加上const進(jìn)行修飾。?
void Print() const
{
cout << "Print()const" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl << endl;
}
?這樣無論在自定義類型的前面是否加上const進(jìn)行修飾,都可以對(duì)上述函數(shù)進(jìn)行調(diào)用。所以在調(diào)用時(shí),我們可以將一個(gè)變量的權(quán)限放小,但是絕不能進(jìn)行放大。
隨之又會(huì)引出一個(gè)問題:成員函數(shù)有const進(jìn)行修飾,無論實(shí)參有無const都能進(jìn)行調(diào)用,那我們需不需要將所以的成員函數(shù)都加上const呢?
其實(shí)是不用的,我們加上const進(jìn)行修飾的this指針指向的內(nèi)容不被修改,如果我們的成員函數(shù)需要修改this指針?biāo)赶虻膬?nèi)容,我們就不用去加const。
Date operator++(int);
Date& operator+=(int day);
Date& operator-=(int day);
Date& operator++();
Date& operator--();
Date operator--(int);
比如上述的運(yùn)算符重載就不用加const,因?yàn)檫@些都是改變this指向的內(nèi)容的。
bool Date::operator>(const Date& y)
{if (_year > y._year){return true;}else if (_year == y._year && _month > y._month){return true;}else if (_year == y._year && _month == y._month && _day > y._day){return true;}return false;
}
int main()
{
Date s1();
const Date s2();
s1 < s2;//正確
s2 < s1;//報(bào)錯(cuò)
return 0;
}
?上述代碼是<的運(yùn)算符重載,在之前的博客中我們已經(jīng)進(jìn)行了復(fù)現(xiàn),但是當(dāng)我們的參數(shù)類型一個(gè)被const修飾,另一個(gè)沒有const修飾當(dāng)我們調(diào)用此函數(shù)s2?< s1時(shí)就會(huì)出現(xiàn)報(bào)錯(cuò),因?yàn)椴荒軐?shí)參的權(quán)限進(jìn)行放大,也就是參數(shù)類型不匹配,所以這種類似內(nèi)容的函數(shù)就必須加上const進(jìn)行修飾。
void Print() const;
bool operator==(const Date& y) const;
bool operator!=(const Date& y) const;
bool operator>(const Date& y) const;
bool operator<(const Date& y) const;
bool operator>=(const Date& y) const;
bool operator<=(const Date& y) const;
int operator-(const Date& d) const;
Date operator+(int day) const;
Date operator-(int day) const;
總結(jié):
1.能定義const的成員函數(shù)都應(yīng)該定義成const,這樣const成員與非const成員都可以進(jìn)行調(diào)用。調(diào)用條件(權(quán)限平移)(權(quán)限縮小)。
2.要修改成員變量的函數(shù)不能定義const。
?取地址及const取地址操作符重載
取地址操作運(yùn)算符重載也是六大默認(rèn)函數(shù)之一,通過重定義對(duì)對(duì)象進(jìn)行取地址操作就是取地址操作符的重載。這兩個(gè)默認(rèn)成員函數(shù)一般不用重新定義 ,編譯器默認(rèn)會(huì)生成。為什么會(huì)是兩個(gè)呢?因?yàn)橛袩oconst是有區(qū)別的,他們會(huì)形成函數(shù)重載。
Date* operator&()
{cout << "Date* operator&()" << endl;return this;}const Date* operator&()const
{cout << "const Date* operator&()const" << endl;return this;}
int main()
{// const對(duì)象和非const對(duì)象都可以調(diào)用const成員函數(shù)const Date d1(2023, 10, 31);d1.Print();Date d2(2023, 1, 1);d2.Print();cout << &d1 << endl;cout << &d2 << endl;return 0;
}
這里許多人就會(huì)有疑問,這里不會(huì)產(chǎn)生二義性嗎?針對(duì)cout << &d2?<< endl;因?yàn)閐2沒有被const修飾,所以既可以調(diào)用理論上來說兩個(gè)函數(shù)都可以進(jìn)行調(diào)用。但是C++會(huì)優(yōu)先匹配最合適的類型,因?yàn)閐2沒有被const進(jìn)行修飾,所以優(yōu)先會(huì)調(diào)用沒有被const修飾的函數(shù)。
如果將沒有const修飾的函數(shù)進(jìn)行屏蔽,兩種實(shí)參照樣可以進(jìn)行調(diào)用。
再談構(gòu)造函數(shù)
之前我們就講過構(gòu)造函數(shù)已經(jīng)將了有80%了,現(xiàn)在我們將構(gòu)造函數(shù)中剩下的20%進(jìn)行收尾。我們先來復(fù)習(xí)一下之前的構(gòu)造體系:
class Date
{
public:
Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}
private:
int _year;
int _month;
int _day;
};
這是函數(shù)體內(nèi)初始化,我們進(jìn)行對(duì)象的初始化時(shí)就會(huì)調(diào)用此函數(shù),當(dāng)我們沒有構(gòu)造函數(shù)時(shí),我們就會(huì)調(diào)用C++提供的默認(rèn)構(gòu)造函數(shù)進(jìn)行匹配。
構(gòu)造函數(shù)的特征:
1. 函數(shù)名與類名相同。
2. 無返回值。
3. 對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
4. 構(gòu)造函數(shù)可以重載。
雖然上述構(gòu)造函數(shù)調(diào)用之后,對(duì)象中已經(jīng)有了一個(gè)初始值,但是不能將其稱為對(duì)對(duì)象中成員變量
的初始化,構(gòu)造函數(shù)體中的語(yǔ)句只能將其稱為賦初值,而不能稱作初始化。因?yàn)槌跏蓟荒艹跏?br /> 化一次,而構(gòu)造函數(shù)體內(nèi)可以多次賦值。
現(xiàn)在我們還有一種可以初始化的辦法:
Date(int year, int month, int day):_year(year),_month(month) ,_day(day),_ref(year),_n(1){// 初始化列表}
這樣的初始化我們稱之為初始化列表。
初始化列表
初始化列表:以一個(gè)冒號(hào)開始,接著是一個(gè)以逗號(hào)分隔的數(shù)據(jù)成員列表,每個(gè)"成員變量"后面跟
一個(gè)放在括號(hào)中的初始值或表達(dá)式。
class Date
{
public:
Date(int year, int month, int day)
: _year(year), _month(month), _day(day)
{}
private:
int _year;
int _month;
int _day;
};
這樣寫我們照樣可以進(jìn)行初始化。這兩種方法都可以進(jìn)行初始化,他們的區(qū)別在哪呢?
上述的例子使用兩種初始化都可以,但是有些成員變量就只能使用初始化列表進(jìn)行初始化。因?yàn)樵陬愔兴接谐蓡T都只是聲明,沒有開辟空間,而特殊的成員變量只能在定義的時(shí)候進(jìn)行賦值,比如:引用、const修飾……所以我們要在初始化列表進(jìn)行定義。
在內(nèi)置類型中構(gòu)造函數(shù)將內(nèi)置類型進(jìn)行賦隨機(jī)值,而特殊內(nèi)置類型只能賦值一次所以不能再被改變,所以我們就要一次性將其賦值好!!!
class A
{
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
class B
{
public:
B(int a, int ref)
:_aobj(a)
,_ref(ref)
,_n(10)
{}
private:
A _aobj; ?// 沒有默認(rèn)構(gòu)造函數(shù)
int& _ref; ?// 引用
const int _n; // const
};
所以引用成員變量、const成員變量、自定義類型成員(且該類沒有默認(rèn)構(gòu)造函數(shù)時(shí))都要進(jìn)行初始化列表賦值。
當(dāng)我們?nèi)ザx一種自定義類型時(shí),如果沒有對(duì)應(yīng)的構(gòu)造函數(shù),程序就會(huì)報(bào)錯(cuò)。所以當(dāng)我們定義一個(gè)類嵌套在另一個(gè)類時(shí),在創(chuàng)建類的構(gòu)造函數(shù)時(shí)創(chuàng)建成全缺省參數(shù)的構(gòu)造函數(shù)。
下面給大家看一個(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();
}
A. 輸出1 ?1
B.程序崩潰
C.編譯不通過
D.輸出1 ?隨機(jī)值
這道題應(yīng)該選D,這是為什么呢?成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關(guān)?,所以_a2是在私有成員中先聲明的,所以在初始化中先定義_a2,因?yàn)開a1在后面所以先為隨機(jī)值,所以_a2為隨機(jī)值,_a1為1.
最后我們來總結(jié)一下初始化列表解決的問題:
1、必須在定義的地方顯示初始化 ?引用 ?const ? 沒有默認(rèn)構(gòu)造自定義成員
2、有些自定義成員想要顯示初始化,自己控制
3.? ?盡量使用初始化列表初始化
4. 構(gòu)造函數(shù)能不能只要初始化列表,不要函數(shù)體初始化
答:不能,因?yàn)橛行┏跏蓟蛘邫z查的工作,初始化列表也不能全部搞定
class Stack
{
public:Stack(int n = 2):_a((int*)malloc(sizeof(int)* n)), _top(0), _capacity(n){//...//cout << "Stack(int n = 2)" << endl;if (_a == nullptr){perror("malloc fail");exit(-1);}memset(_a, 0, sizeof(int) * n);}
當(dāng)我們進(jìn)行動(dòng)態(tài)內(nèi)存開辟時(shí),我們就需要進(jìn)行函數(shù)內(nèi)外的配合,因?yàn)樵诔跏蓟斜碇胁荒苓M(jìn)行其他操作,而在函數(shù)體內(nèi)可以,為了避免開辟失敗,我們需要進(jìn)行指針的檢查,以及其他操作。所以80-100%初始化列表搞定,還有需要用函數(shù)體,他們可以混著用
以上就是本次全部?jī)?nèi)容,感謝大家觀看!!!