1. 導(dǎo)讀
我們的目標(biāo)
- 在先前基礎(chǔ)課程所培養(yǎng)的正規(guī)、大氣的編程素養(yǎng)上,繼續(xù)探討更多技術(shù)。
- 泛型編程(Generic Programming)和面向?qū)ο缶幊蹋∣bject-Oriented Programming)雖然分屬不同思維,但它們正是 C++的技術(shù)主線,所以本課程也討論template(模板)。
- 深入探索面向?qū)ο蟮睦^承關(guān)系(inheritance)所形成的對(duì)象模型(Object Model),包括隱藏于底層的 this 指針,vptr(虛指針),vtbl(虛表),virtual mechanism(虛機(jī)制),以及虛函數(shù)(virtual functions)造成的 polymorphism(多態(tài))效果。
2. 類(lèi)型轉(zhuǎn)換
轉(zhuǎn)換有兩種,一種是轉(zhuǎn)出去,一種是轉(zhuǎn)過(guò)來(lái)。
2.1 Conversion Function 轉(zhuǎn)換函數(shù)
轉(zhuǎn)換函數(shù)的作用是轉(zhuǎn)出去,把這個(gè)類(lèi)的值轉(zhuǎn)換成其他的類(lèi)型。
operator double() const {……;}
定義了一個(gè)轉(zhuǎn)換為 double 的函數(shù),
沒(méi)有參數(shù),轉(zhuǎn)換時(shí)不會(huì)帶有參數(shù)。
不寫(xiě)返回類(lèi)型,返回類(lèi)型就是名稱(chēng)里這個(gè)double
- 如果不改變值,就該加 const,否則后面可能會(huì)出錯(cuò)。
- 只要認(rèn)為合理,可以設(shè)計(jì)好幾個(gè)轉(zhuǎn)換函數(shù)。
模板的偏特化?
操作符重載
2.2 只有一個(gè)參數(shù)的構(gòu)造函數(shù)
只有一個(gè)參數(shù)的構(gòu)造函數(shù)可以將一個(gè) int 或 float 值轉(zhuǎn)換為該類(lèi)對(duì)象。
2.2.1 non-explicit-on-argument ctor
Fraction (int num, int den=1): m_numerator(num), m_denominator(den) { }
分母默認(rèn)是1,這樣一個(gè)值構(gòu)造為對(duì)象時(shí),實(shí)際值不變。
Fraction operator+(const Fraction& f) { }
只能分?jǐn)?shù)加分?jǐn)?shù)
Fraction d2=f+4;
4可以轉(zhuǎn)換為 Fraction,因?yàn)橛幸粋€(gè)構(gòu)造函數(shù)只有一個(gè)int 參數(shù)。
2.2.2 conversion function vs. non-explicit-one argument ctor
當(dāng)轉(zhuǎn)換函數(shù)與非顯式聲明的一個(gè)參數(shù)的構(gòu)造函數(shù)同時(shí)存在時(shí),就會(huì)出現(xiàn)歧義。因?yàn)閮煞N方式都可以編譯。
多于一條路徑可以編譯,就會(huì)出現(xiàn)歧義,編譯器就會(huì)報(bào)錯(cuò)。
[Error] ambiguous
2.2.3 explicit-one-argument ctor
顯式聲明就可以避免這個(gè)問(wèn)題。
explicit Fraction(int num, int den=1):……
此時(shí)就會(huì)調(diào)用構(gòu)造函數(shù),而不會(huì)調(diào)用 double 轉(zhuǎn)換函數(shù)。
3. 模仿的類(lèi)
3.1 pointer-like classes
設(shè)計(jì)一個(gè)類(lèi),模擬 pointer
3.1.1 關(guān)于智能指針
->
用掉后,還有一個(gè)->
,所以
sp->method();
px->method(); //轉(zhuǎn)換完依舊有->
3.1.2 關(guān)于迭代器
注意操作符重載
++
--
適用于指針移動(dòng)return (*node).data; //取的是node 指向的塊的數(shù)據(jù)
return &(operator()); //返回迭代器中內(nèi)容的指針,而不是指向迭代器塊的 node 的指針。
3.2 function-like classes,所謂仿函數(shù)
unary_function 一個(gè)操作數(shù)
binary_function 兩個(gè)操作數(shù)
4. namespace 經(jīng)驗(yàn)談
namespace jj01
{
}//namespace
5. 模板 template
5.1 class template
template<typename T>
5.2. Function Template
template <class T>
5.3 Member Template
template <class T1, class T2>
struct pair {
……
template <class U1, class U2>
……
}
Base1* ptr = new Derived1; //up-cast
shared_ptr<Base1>sptr(new Derived1); // 模擬 up-cast
5.4 specialization,模板特化
template<class Key>
struct hash { } ;
template<>
struct hash<char> { };
泛化對(duì)應(yīng)特化,共性中的個(gè)性,用來(lái)應(yīng)對(duì)特例。
5.4.1 partial specialization,模板偏特化
又稱(chēng)為局部特化
5.4.1.1 個(gè)數(shù)的偏
< >
尖括號(hào)內(nèi)叫做模板參數(shù)
class vector<bool, Alloc> { }; // Alloc沒(méi)改變,而 bool 設(shè)定了。
一定要從左到右,不能跳,不能135固定,24改變。
5.4.1.2 范圍的偏
指針指向的偏
class c<T*> { }; // 限定為指針
如果 T 是指針,使用偏特化模板。
5.5 template template parameter,模板模板參數(shù)
5.5.1 容器需要參數(shù)
using Lst = list<T, allocator<>>;
模板需要好幾個(gè)參數(shù),必須替換
5.5.2 智能指針
對(duì)應(yīng) SmartPtr,可以是……,不可以是……
5.5.3 這不是模板模板參數(shù)
已經(jīng)綁定了,必須是這個(gè),所以就沒(méi)有模糊地帶了。
6. 關(guān)于 C++ 標(biāo)準(zhǔn)庫(kù)
所有的容易算法都要用一遍。
編譯器需要設(shè)定到 C++ 11
6.1 variadic templates (since C++11)
6.2 auto (since C++11)
用 auto 時(shí)一定要讓編譯器能推出來(lái)。
auto ite = find(……)
find 的類(lèi)型就是 auto 給 ite 的類(lèi)型。
auto 不能亂用,太長(zhǎng)了、寫(xiě)不出來(lái)可以用。
我們一定要知道每個(gè)變量的定義是什么。
ranged-bas for (since C++11)
for ( decl : coll ) //左邊是一個(gè)變量,右邊必須是一個(gè)collector,容器
for ( int i : { 2, 3, 5, 7 } ){ }
vector<double> vec;
……
for ( auto elem : vec ) { cout << elem << endl ;} //傳值
for ( auto& elem : vec ) { elem *= 3; } //傳引用,盡量傳引用
15. reference(引用)
聲明時(shí)一定要有初值,設(shè)完之后就不能再變了。
object 和其 reference 的大小相同,地址也相同(全都是假象)
???那么新建一個(gè) reference 會(huì)讓程序新占用多少內(nèi)存呢?
常見(jiàn)用途
函數(shù)傳參
void func3(Cls& obj) { obj.xxx(); }
func3(obj);
reference 通常不用于聲明變量,而用于參數(shù)類(lèi)型和返回類(lèi)型的描述。
相同聲明結(jié)構(gòu),僅僅傳遞引用和變量,會(huì)導(dǎo)致模糊,簽名相同,所以不能同時(shí)存在。
是否加const,
???有什么區(qū)別?
加 const 會(huì)改變簽名,是可以定義的。
7. 復(fù)合&繼承關(guān)系下的構(gòu)造和析構(gòu)
7.1 Composition(復(fù)合)關(guān)系下的構(gòu)造和析構(gòu)
Container 在外 Component 在內(nèi)
7.2 Inheritance(繼承)關(guān)系下的構(gòu)造和析構(gòu)
Derive在外,Base 在內(nèi)
由內(nèi)而外構(gòu)建
由外而內(nèi)析構(gòu)
子類(lèi)析構(gòu)函數(shù)會(huì)自動(dòng)調(diào)用父類(lèi)析構(gòu)函數(shù)
Inheritance + Composition 關(guān)系下的構(gòu)造和析構(gòu)
不同編譯器可能不同
觀察到的結(jié)論是先調(diào)用 Base,后調(diào)用 Component