雖然C++已經(jīng)不是我的主要工具了, 但是還是重新復(fù)習(xí)了下, 有些內(nèi)容之前沒(méi)搞懂。
class vs struct
以前的C里是沒(méi)有class這個(gè)關(guān)鍵字的怯邪,struct只用于一些數(shù)據(jù)類(lèi)型的結(jié)合绊寻。在C++里,這兩個(gè)關(guān)鍵字的唯一區(qū)別是默認(rèn)訪問(wèn)權(quán)限不同悬秉。
- class默認(rèn)是private
- struct默認(rèn)是public
我的認(rèn)知里, struct仍然只有一般數(shù)據(jù)聚合的作用澄步,并且最好是定長(zhǎng)的,不包含任何其他方法和泌,而class則用于定義類(lèi)
this指針
成員函數(shù)可以通過(guò)this指針來(lái)訪問(wèn)調(diào)用它的對(duì)象村缸,在成員函數(shù)內(nèi)部可以省略
std::string func() { return this->blog; }
const成員函數(shù)
在類(lèi)的成員函數(shù)末尾的cost關(guān)鍵字,可以修改this指針為const指針武氓,可以避免成員函數(shù)修改類(lèi)成員變量
std::string func() const { return this->blog; }
默認(rèn)構(gòu)造函數(shù)
如果沒(méi)有為對(duì)象提供構(gòu)造函數(shù)梯皿,則編譯器會(huì)自動(dòng)創(chuàng)建構(gòu)造函數(shù),稱(chēng)為合成的默認(rèn)構(gòu)造函數(shù)县恕。 合成默認(rèn)構(gòu)造函數(shù)規(guī)則:
- 如果存在類(lèi)內(nèi)的初始值东羹,則用初始值初始化類(lèi)的數(shù)據(jù)成員
- 默認(rèn)初始化該成員
某些類(lèi)不能依賴(lài)于合成的默認(rèn)構(gòu)造函數(shù):
- 編譯器只有在發(fā)現(xiàn)類(lèi)不包含任何構(gòu)造函數(shù)的情況下才會(huì)生成默認(rèn)構(gòu)造函數(shù)
- 合成的默認(rèn)構(gòu)造函數(shù)可能執(zhí)行錯(cuò)誤的操作(內(nèi)置類(lèi)型或復(fù)合類(lèi)型默認(rèn)初始化,則它們的值將是未定義的忠烛,似乎和C的兼容有關(guān))
- 有時(shí)編譯器不能為某些類(lèi)提供默認(rèn)構(gòu)造函數(shù)属提,因?yàn)轭?lèi)中包含的其他類(lèi)型的成員包含了非默認(rèn)構(gòu)造函數(shù)
=default的含義
class FileData{
std::string _fileNo;
FileData() = default;
FileData(const std::string &s) : _fileNo(s) {}
}
該構(gòu)造函數(shù)(default)不接收任何參數(shù),所以是默認(rèn)構(gòu)造函數(shù)美尸。這個(gè)構(gòu)造函數(shù)完全等同于合成默認(rèn)構(gòu)造函數(shù)冤议,并且如果在類(lèi)內(nèi)部定義則是內(nèi)聯(lián)的,在外部定義則不是
構(gòu)造函數(shù)初始化列表
FileData(const std::string &s): fileNo(s) {}
冒號(hào)和花括號(hào)間的代碼為構(gòu)造函數(shù)初始值列表师坎,負(fù)責(zé)為新創(chuàng)建的對(duì)象的數(shù)據(jù)成員賦初值恕酸。
- 構(gòu)造函數(shù)不應(yīng)該輕易覆蓋類(lèi)內(nèi)初始值
- 如果你不能使用類(lèi)內(nèi)初始值,則所有構(gòu)造函數(shù)均應(yīng)該顯示初始化每個(gè)內(nèi)置類(lèi)型
委托構(gòu)造函數(shù)
這個(gè)是C++11里面的新功能胯陋,放上例子蕊温。在委托構(gòu)造函數(shù)中袱箱,先執(zhí)行其本身的代碼(花括弧中的...),再去執(zhí)行被委托的構(gòu)造方法
class FileData {
//非委托構(gòu)造函數(shù)
FileData(std::string s){}
FileData(std::string s, unsigned count):fileNo(s), unitsSold(count){}
//委托構(gòu)造函數(shù)
FileData(): FileData("",0){...}
void combine(const FileData& item){}
}
explicit關(guān)鍵字
explicit關(guān)鍵字主要是用在隱式轉(zhuǎn)換上寿弱。比如說(shuō)item是一個(gè)FileData對(duì)象犯眠,該對(duì)象有一個(gè)combine方法,接受一個(gè)FileData類(lèi)型的常量引用症革。那么
//顯式轉(zhuǎn)換成string,隱式轉(zhuǎn)化成FileData鸯旁,正確
item.combine(string("6666"));
//隱式轉(zhuǎn)換成string噪矛,顯式轉(zhuǎn)換成SalesData,正確
item.combine(FileData("6666"));
//但是不能兩步轉(zhuǎn)換铺罢!錯(cuò)誤艇挨!
item.combine("6666");
而explicit關(guān)鍵字能阻止隱式轉(zhuǎn)換(顧名思義嘛,explicit是明確的意思)
不過(guò)要注意一點(diǎn)的是韭赘,explicit關(guān)鍵字只對(duì)單個(gè)參數(shù)的構(gòu)造函數(shù)有效缩滨,需要多個(gè)參數(shù)的構(gòu)造函數(shù)不適用隱式轉(zhuǎn)換,因此也就不需要explicit關(guān)鍵字了泉瞻。
類(lèi)的拷貝脉漏、賦值、析構(gòu)
大多數(shù)情況下編譯器會(huì)自動(dòng)生成默認(rèn)的拷貝袖牙、賦值侧巨、析構(gòu)函數(shù),但是也不盡然鞭达,尤其是當(dāng)類(lèi)內(nèi)有動(dòng)態(tài)分配的內(nèi)存時(shí)(比如涉及到new delete之類(lèi)的)司忱。事實(shí)上為了防止我們?cè)趦?nèi)存的槍林彈雨里掙扎,《C++ Primer》建議能用std里的東西就用std里的東西畴蹭,比如vector或string之類(lèi)的坦仍。
類(lèi)拷貝
拷貝構(gòu)造函數(shù)指的是:一個(gè)構(gòu)造函數(shù)的第一個(gè)參數(shù)是自身類(lèi)型的引用,且任何額外參數(shù)都有默認(rèn)值
class Foo {
public:
Foo();
Foo(const Foo&);
}
因此對(duì)于類(lèi)而言
- 直接初始化:要求編譯器使用普通的函數(shù)匹配
- 拷貝初始化:將右側(cè)的運(yùn)算對(duì)象拷貝到正在創(chuàng)建的對(duì)象中(可能還有類(lèi)型轉(zhuǎn)換)
值得注意的是叨襟,拷貝初始化不僅在使用=的時(shí)候發(fā)生繁扎,還包括了:
- 將一個(gè)對(duì)象作為實(shí)參傳遞給一個(gè)非引用類(lèi)型形參
- 從一個(gè)返回類(lèi)型為非引用類(lèi)型的函數(shù)返回一個(gè)對(duì)象
- 用花括弧列表初始化一個(gè)數(shù)組中的元素或一個(gè)聚合類(lèi)中的成員
前兩點(diǎn)比較好理解。在我的個(gè)人實(shí)踐中芹啥,為了防止超大的vector在函數(shù)調(diào)用中引起的內(nèi)存拷貝锻离,一般這么寫(xiě)(不知道有沒(méi)有更優(yōu)雅的方式)
void fun(const vector<int> &arg, vector<int> &res);