??個人主頁:Forcible Bug Maker
??專欄:C++
目錄
前言
取地址及const取地址操作符重載
再談構造函數(shù)
初始化列表
隱式類型轉(zhuǎn)換
explicit關鍵字
成員變量缺省值
結語
前言
本篇主要內(nèi)容:類的六個默認成員函數(shù)中的取地址及const取地址重載,構造函數(shù)初始化列表,隱式類型轉(zhuǎn)換,缺省值。
上篇博客用之前學過的知識實現(xiàn)了一個簡單的日期類Date,在日期類中,有介紹到多種類型運算符重載的運用,如前置++后置++等。在運算符重載的過程中,有效的代碼復用也非常重要,可以大大簡化代碼編寫過程。最后還提到了const成員和友元。本篇博客將會介紹最后兩個類的默認成員函數(shù),不過并不困難。而文中再次談到的構造函數(shù)需要靜下心來理解。
取地址及const取地址操作符重載
這兩個默認成員函數(shù)一般不用重新定義,編譯器默認生成的就夠用。
class Date
{
public:
Date(int year = 2000, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 取地址重載
Date* operator&()
{
return this;
}
// const取地址重載
const Date* operator&()const
{
return this;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{
Date d1;
const Date d2;
cout << &d1 << endl;
cout << &d2 << endl;
return 0;
}
取地址重載,其實就是返回地址的兩個函數(shù),C++提供這種默認成員函數(shù)主要是想兼容操作符重載,給予C++更大的靈活性。在上面的代碼案例中,d1取地址時調(diào)用的是非const類型的取地址重載函數(shù),而d2取地址時調(diào)用的是const類型的取地址重載函數(shù)。我們可以改變返回值再去觀察一下。
這次我們調(diào)整返回值后再打印,是否能感受到關于取地址重載的運用呢?其實,取地址重載很少用,除非你要惡作劇或者想讓別人獲取到指定的內(nèi)容,否則默認生成的取地址就是完全夠用的。
再談構造函數(shù)
初始化列表
C++的初始化列表(Initializer List)是構造函數(shù)的一種特性,用于初始化類的數(shù)據(jù)成員。在構造函數(shù)體執(zhí)行之前,初始化列表會先執(zhí)行,確保數(shù)據(jù)成員在構造函數(shù)體開始執(zhí)行之前就已經(jīng)被正確地初始化。
初始化列表的使用:以一個冒號開始,接著是一個以逗號分隔的數(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;
};
當使用上述類初始化對象時,三個成員函數(shù)都會成功在初始化列表中被傳入的參數(shù)初始化。你可能會問,為什么要有初始化列表,在構造函數(shù)的函數(shù)體中初始化不是很香嗎?可以來看看下面這個例子:
class stack
{
public:
stack(int capacity = 4)
{
_a = (int*)malloc(sizeof(int) * capacity);
_size = 0;
_capacity = capacity;
}
void push(int x)
{
_a[_size++] = x;
}
private:
int* _a;
int _size;
int _capacity;
};
class MyQueue
{
public:
MyQueue(int pushN, int popN)
{}
private:
stack _pushst;
stack _popst;
int _size;
};
在上面這份代碼中,我們編寫了一個MyQueue類,里面定義了兩個對象成員和一個整型成員變量,你是否想過,該如何初始化對象成員呢?由于stack中定義了缺省參數(shù),不需要傳參就可以完成構造,但如果你需要指定stack的capacity或者沒有缺省參數(shù)時,該怎么辦呢?
仔細思考,進入函數(shù)體后,成員變量的空間就已經(jīng)都開好了,所以在函數(shù)體中是無法完成初始化賦值的。而初始化列表就可以完美解決此問題。如下是初始化列表初始化對象的方式:
以下三種類的成員,必須放在初始化列表的位置進行初始化:
- 引用成員變量
- 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
};
建議:能在初始化列表中初始化就在初始化列表中初始化,因為不管你是否使用初始化列表,對于自定義類型成員變量,一定會先使用初始化列表初始化。
初始化列表的特點:
- 初始化列表,不管寫沒寫,每個成員變量都會走一遍,而且在初始化列表中只能出現(xiàn)一次(初始化只能初始化一次)
- 對于自定義類型,會調(diào)用默認構造(沒有默認構造則報錯)。
- 先走初始化列表,再走函數(shù)體。
- 拷貝構造也有初始化列表。
- 成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關。
對于第四點,我們可以使用一份代碼來證明:
上面這份代碼,根據(jù)_a1和_a2的聲明順序,初始化列表先走的_a2,再走的_a1,導致在初始化_a2使用了未初始化_a1,故產(chǎn)生了隨機值,佐證了特點四。
隱式類型轉(zhuǎn)換
之前我們講過,不同類型的內(nèi)置類型變量在相互賦值時會有隱式類型轉(zhuǎn)換。
double a = 10.5;
int b = a;
就如上面這個簡單的賦值,在a賦值給b之前,會產(chǎn)生一個臨時變量,最終賦給b值的就是這個臨時變量。
當將不同類型的變量取引用時,需要加const的原因,是因為臨時變量具有常性。
臨時變量具有常性,其本質(zhì)就跟數(shù)字一樣如,1,2,3等,可以給變量賦值,正常情況下不能取到地址或者取到引用,除非用const修飾變量。
double a = 10.5;
// int& b = a;// 報錯
// int& c = 10;// 報錯
const int& b = a;// 正常運行
const int& c = 10;// 正常運行
上述代碼中b取的就是a產(chǎn)生的臨時變量的引用,臨時變量存儲在內(nèi)存的靜態(tài)區(qū),具有常性,就跟第四行代碼的數(shù)字10性質(zhì)是一樣的,當你加上const時,這種引用權限就被放開了,因為const確保了你不會對靜態(tài)區(qū)的變量做出改動。對于C++的自定義類型,與內(nèi)置類型遵循的規(guī)則是一樣的。
C++支持一種類型轉(zhuǎn)換式的構造:
class A
{
public:
A(int a)
:_a1(a)
{}
A(const A& aa)
:_a1(aa._a1)
{
cout << "A(const A& aa)" << endl;
}
private:
int _a1;
int _a2;
};
int main()
{
A aa1(1);
A aa2 = 1;
return 0;
}
對于main函數(shù)第一行代碼是標準的調(diào)用了構造函數(shù)。而第二行,作為內(nèi)置類型的1,竟然能給對象的初始化賦值,這是因為在賦值之前,產(chǎn)生了隱式類型轉(zhuǎn)換,1作為一個參數(shù)傳遞給了構造函數(shù),從而產(chǎn)生了一個臨時對象,最終臨時變量拷貝構造給aa2。
在調(diào)用此代碼的過程中,我們發(fā)現(xiàn),并沒有調(diào)用拷貝構造函數(shù),這是因為通過編譯器的優(yōu)化,省去了拷貝構造這一過程,簡單來說就是:
構造函數(shù) + 拷貝構造 + 編譯器優(yōu)化 = 構造函數(shù)
這時候看這兩行能否運行的原因應該就不困難了:
// A& ref = 10;// 報錯
const A& ref = 10;//可運行
// 這里ref引用的是類型轉(zhuǎn)換中用10構造的臨時對象
在上面代碼中,我們使用的構造函數(shù)一直是單參數(shù)的,可以使用特殊的隱式類型轉(zhuǎn)換構造。但是如果構造函數(shù)是多參數(shù)的,該怎么使用類似于A aa = 1;的方式創(chuàng)建對象呢?其實C++提供了解決方案,那就是多參數(shù)構造。
class A
{
public:
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{}
A(const A& aa)
:_a1(aa._a1)
,_a2(aa._a2)
{}
void print()const
{
cout << _a1 << " " << _a2 << endl;
}
private:
int _a1;
int _a2;
};
// 多參數(shù)構造
int main()
{
A aa1 = { 2,2 };
aa1.print();
// A& ref = { 2,3 };//報錯
const A& ref = { 2,3 };
ref.print();
return 0;
}
不過需要注意的是,只有C++11及其往后的版本才支持多參數(shù)構造。老版本,如C++98并不支持這樣創(chuàng)建對象。
explicit關鍵字
這個知識點稍稍提一下,如果不想允許構造時出現(xiàn)類的隱式類型轉(zhuǎn)換,可以在拷貝構造前加個explicit關鍵字,就可以成功限制類的隱式類型轉(zhuǎn)換了。
關于explicit的更多使用,在后面有機會還會講。
成員變量缺省值
之前講過,在C++11的新標準中,支持為類中的成員變量提供缺省值。在類和對象中,提供的缺省值是提供給初始化列表使用的。由于支持隱式類型轉(zhuǎn)換構造等原因,提供的缺省值可以非常靈活,見代碼:
class A
{
public:
A(int a1)
:_a1(a1)
{
cout << "A(int a1)" << endl;
}
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{
cout << "A(int a1, int a2)" << endl;
}
A(const A& aa)
:_a1(aa._a1)
, _a2(aa._a2)
{
cout << "A(const A& aa)" << endl;
}
private:
int _a1;
int _a2;
};
class B
{
public:
private:
int _b1 = 1;// 缺省值可以給整型變量
int* ptr = (int*)malloc(40);// 可以開空間給指針
A _aa1 = 1;// 可以給對象類型(A _aa1(1);這樣構造是錯誤的)
A _aa2 = { 1,2 };// 多參數(shù)構造
A _aa3 = _aa2;// 拷貝構造,缺省參數(shù)甚至可以是一個對象
};
int main()
{
B bb1;
return 0;
}
這些缺省參數(shù),最終都會提供給初始化列表。
如果顯示提供了初始化列表,運行時,這些被提供的缺省參數(shù)就會被忽略(簡單說就是:如果既提供了初始化列表,也有缺省值,編譯器默認使用初始化列表提供的值)。
結語
本篇博客將最后兩個默認成員函數(shù)做了一個收尾,再次談到了構造函數(shù)的一些語法和特性,關于初始化列表的概念和使用;一種很新的創(chuàng)建對象方式,隱式類型轉(zhuǎn)換方式創(chuàng)建對象,而explicit關鍵字可以限制這種轉(zhuǎn)換的發(fā)生;最后還提到了C++11的新特性成員變量的缺省值,列出了對象,指針等類型給缺省值的方式。在類和對象的下一篇,會再介紹幾個類和對象的小特性,以及編譯器做出的優(yōu)化。文章來源:http://www.zghlxwxcb.cn/news/detail-858302.html
博主還會繼續(xù)產(chǎn)出有趣的內(nèi)容,感謝大家的支持!?文章來源地址http://www.zghlxwxcb.cn/news/detail-858302.html
到了這里,關于【C++】類和對象④(類的默認成員函數(shù):取地址及const取地址重載 | 再談構造函數(shù):初始化列表,隱式類型轉(zhuǎn)換,缺省值)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!