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