(GeekBand)C++面向?qū)ο蟾呒壘幊蹋ㄏ拢┑谝恢芄P記(1)

第一節(jié) 導(dǎo)讀

知識清單:

首先來列一張清單,清點(diǎn)一下后面課程將會深入的細(xì)節(jié):

  • operator type()const;
  • explicit complex(...):initialization list{}
  • pointer-like object
  • function-like object
  • Namespace
  • template specialization
  • Standard Library
  • variadic template(since C++11)
  • move ctor(since C++11)
  • Rvalue reference(since C++11)
  • auto(since C++11)
  • lambda(since C++11)
  • range-base for loop(since C++11)
  • unordered containers(cince C++11)
目標(biāo):
  • 在先前的基礎(chǔ)課程所培養(yǎng)的正規(guī)蛤奢、大氣的編程素養(yǎng)上俭驮,繼續(xù)探討更多技術(shù)。
  • 泛型編程(Generic Programming)和面向?qū)ο缶幊蹋∣bject-Oriented Programming)雖然分屬不同思維赡若,但它們正是C++的技術(shù)主線达布,所以本課程也討論template(模板)。
  • 深入探索面向?qū)ο笾^承關(guān)系(inheritance)所形成的對象模型(Object Model)逾冬,包括隱藏于底層的this指針黍聂,vptr(虛指針),vtbl(虛表)身腻,virtual mechanism(虛機(jī)制)产还,以及虛函數(shù)(virtual functions)造成的polymorphism(多態(tài))效果。
推薦書目
  • 《C++ Primer》
  • 《The C++ Programming Language》
  • 《Effective Modern C++》
  • 《Effective C++》
  • 《The C++ Standard Library》
  • 《STL源碼剖析》

第二嘀趟、三節(jié) non-explicit one argument constructor & Conversion Function(轉(zhuǎn)換構(gòu)造與類型轉(zhuǎn)換函數(shù))

為了方便對照學(xué)習(xí)脐区,記憶,決定把二三節(jié)內(nèi)容放在一起講解她按。

轉(zhuǎn)換構(gòu)造函數(shù)

定義

在CPP中牛隅,類的構(gòu)造函數(shù)可以省略不寫,這時CPP會為它自動創(chuàng)建一個隱式默認(rèn)構(gòu)造函數(shù)(implicit default constructor)酌泰;也可以由用戶定義帶參數(shù)的構(gòu)造函數(shù)媒佣,構(gòu)造函數(shù)也是一個成員函數(shù),他可以被重載陵刹;當(dāng)一個構(gòu)造函數(shù)只有一個參數(shù)默伍,而且該參數(shù)又不是本類的const引用時,這種構(gòu)造函數(shù)稱為轉(zhuǎn)換構(gòu)造函數(shù)(non-explicit ont argument constructor)衰琐。(該段引自百度百科)

class Complex
{
private:
    double real,imag; //復(fù)數(shù)的實(shí)部和虛部
public:
    Complex(double x)
    { 
        real=x;
        imag=0;
    }
    //與下方等價(jià)
    /*
    Complex(double x,double y=0):real(x),imag(y)
    {
    
    }
    */
};

這個構(gòu)造函數(shù)即 轉(zhuǎn)換構(gòu)造函數(shù)也糊。
如上文。構(gòu)造函數(shù)只有一個參數(shù) double x羡宙,它也不是本類的const引用狸剃。

應(yīng)用

通過轉(zhuǎn)換構(gòu)造函數(shù)可以將一個指定類型的數(shù)據(jù)轉(zhuǎn)換為類的對象。

1.用于定義

轉(zhuǎn)換構(gòu)造函數(shù)一般由系統(tǒng)自動調(diào)用(當(dāng)然代碼里自己調(diào)用完全沒問題)狗热,這點(diǎn)很利于編程捕捂。
例如:

  • Complex t=5.0;
  • Complex t(5.0);
  • Complex t=Complex(5.0);
  • Complex t=(Complex)5.0;

這時系統(tǒng)就自動調(diào)用了 Complex(double x)將 5.0轉(zhuǎn)換成Complex類瑟枫,再賦值給t。

2.用于計(jì)算

通常來講指攒,轉(zhuǎn)換構(gòu)造函數(shù)更多搭配運(yùn)算符重載用來計(jì)算慷妙。

class Complex
{
public:
    Complex(double x,double y=0)//轉(zhuǎn)換構(gòu)造
    :real(x),imag(y){}
    Complex operator+(const Complex& f)//操作符重載
    {
        return Complex(......);
    }
private:
    double real;
    double imag;
};
Complex t=5.0;
Complex b=t + 4.8;

編譯器會隱式調(diào)用轉(zhuǎn)換構(gòu)造函數(shù)將5.0轉(zhuǎn)換為Complex成員并賦值給t。第二步同理允悦,將4.8轉(zhuǎn)換成Complex成員后調(diào)用'+'的重載函數(shù)完成計(jì)算膝擂。

類型轉(zhuǎn)換函數(shù)

通過轉(zhuǎn)換構(gòu)造函數(shù)可以將一個指定類型的數(shù)據(jù)轉(zhuǎn)換為類的對象。但是不能反過來將一個類的對象轉(zhuǎn)換為一個其他類型的數(shù)據(jù)(例如將一個Complex類對象轉(zhuǎn)換成double類型數(shù)據(jù))隙弛。

C++提供類型轉(zhuǎn)換函數(shù)(type conversion function)來解決這個問題架馋。類型轉(zhuǎn)換函數(shù)的作用是將一個類的對象轉(zhuǎn)換成另一類型的數(shù)據(jù)。如果已聲明了一個Complex類全闷,可以在Complex類中這樣定義類型轉(zhuǎn)換函數(shù):

operator double() const//類型轉(zhuǎn)換函數(shù)
{
    return real;
}

從函數(shù)結(jié)構(gòu)來看叉寂,與重載函數(shù)類似,都需要關(guān)鍵字operator总珠,只不過這里的轉(zhuǎn)換的是類型而已屏鳍,double在Complex類中經(jīng)過重載后,Complex就被賦予了一種新的含義局服,既可以當(dāng)做Complex類型本身使用钓瞭,也可以當(dāng)做double類型來使用。

我們來舉一個簡單的例子:

class Complex
{
public:
    Complex():real(0),imag(0)
    {}
    Complex(double x,double y):real(x),imag(y)
    {}
    operator double() const
    {
        return real;
    }
private:
    double real;
    double imag淫奔;
};
Complex t(5,0);
Complex b=t + 4.8;

此時我們的b=t+4.8運(yùn)算有了另一種解法山涡,即將Complex對象t通過隱式調(diào)用類型轉(zhuǎn)換函數(shù)轉(zhuǎn)換為double對象完成計(jì)算。

小結(jié):

  • 轉(zhuǎn)換構(gòu)造函數(shù)可以將一個指定類型的數(shù)據(jù)轉(zhuǎn)換為類的對象唆迁。
  • 類型轉(zhuǎn)換函數(shù)可以將一個類的對象轉(zhuǎn)換為一個其他類型的數(shù)據(jù)鸭丛。

我們了解了轉(zhuǎn)換構(gòu)造函數(shù)與類型轉(zhuǎn)換函數(shù)可以為Complex b=t+4.8這樣的運(yùn)算提供兩個不同角度的解法,那么如果Complex類同時擁有了兩種函數(shù)唐责,又會怎樣呢系吩?

class Complex
{
public:
    Complex():real(0),imag(0)
    {}
    Complex(double x,double y=0)//轉(zhuǎn)換構(gòu)造
    :real(x),imag(y){}
    Complex(double x,double y):real(x),imag(y)
    {}
    Complex operator+(const Complex& f)//操作符重載
    {
        return Complex(......);
    }
    operator double() const//
    {
        return real;
    }
private:
    double real;
    double imag;
};
Complex t(5,0);
Complex b=t + 4.8;

編譯器會提示ambiguous(歧義)妒蔚,即有多重解。當(dāng)編譯器可選擇的方案不止一種月弛,會出現(xiàn)這種提示肴盏。在案例中,編譯器既可以通過轉(zhuǎn)換構(gòu)造函數(shù)將4.8轉(zhuǎn)換為Complex對象帽衙,與可以通過類型轉(zhuǎn)換函數(shù)將t轉(zhuǎn)換為double對象完成計(jì)算菜皂。

但是在實(shí)際使用的過程中,難免會遇到這種情況厉萝,好在CPP為我們提供解決的辦法:explicit恍飘。

explicit關(guān)鍵字多用在轉(zhuǎn)換構(gòu)造函數(shù)之前榨崩,其作用是指定該構(gòu)造函數(shù)只能被顯示調(diào)用(即創(chuàng)建實(shí)例時的調(diào)用,如Complex a(5,0))章母,而不可以再被隱式調(diào)用母蛛,這樣就解決了程序的ambiguous問題。

第四節(jié) pointer-like classes(關(guān)于智能指針)

我們知道智能指針能夠比原生指針做更多事情乳怎,例如處理線程安全彩郊,提供寫時復(fù)制,確保協(xié)議蚪缀,并且提供遠(yuǎn)程交互服務(wù)等等等等許許多多強(qiáng)大的功能秫逝。但其實(shí)不論是多牛的智能指針,在它的內(nèi)部一定至少有一個原生指針在工作⊙叮現(xiàn)在就我們從語法的角度來初窺智能指針违帆。

以shared_ptr為例:

template<class T>
class shared_ptr
{
public:
    T& operator*() const//重載*
    {return *px;}
    T* operator->() const//重載->
    {return px;}
    shared_ptr(T* p):px(p){}
private:
    T* px;
    long* pn;
}
struct Foo
{
    void method(){}
};
shared_ptr<Foo> sp(new Foo);
Foo f(*sp);
sp->method();
//px->method();
解析:

智能指針的本質(zhì),其實(shí)就是把一個原生指針包裝在類中金蜀,再向類中寫進(jìn)各種對指針操作符的重載刷后,使用戶在使用類時可以完全按照指針的語法去使用。這樣做的優(yōu)勢很明顯廉油,我們可以根據(jù)自己的需求向類中寫入各種功能惠险,相當(dāng)于“組裝一個無所不能的指針”。

語法其實(shí)不難理解抒线,只是重載一下指針的操作符"*"與"->"班巩,但是其中有一個小細(xì)節(jié)很容易被忽略,及時是有多年經(jīng)驗(yàn)的工程師也未必能解釋清楚嘶炭,在這里再次感謝侯老師抱慌。我舉個例子:

我們已經(jīng)對操作符“*”和“->”進(jìn)行了重載,當(dāng)編譯器在執(zhí)行*sp時眨猎,返回值是*px抑进,這很好理解,可是在執(zhí)行sp->時睡陪,返回值是sp寺渗,為什么能起到和sp->一樣的效果呢?

原來兰迫,CPP為了支持這種做法信殊,在這里進(jìn)行了特殊的處理,使得->可以無限次的使用汁果,即在sp之后自動補(bǔ)齊->涡拘。(注:只有在這種情況下)


看完了shared_ptr,我們再來看看迭代器据德。

迭代器(iterator)是一種對象鳄乏,它能夠用來遍歷標(biāo)準(zhǔn)模板庫容器中的部分或全部元素跷车,每個迭代器對象代表容器中的確定的地址。迭代器修改了常規(guī)指針的接口橱野,所謂迭代器是一種概念上的抽象:那些行為上像迭代器的東西都可以叫做迭代器朽缴。然而迭代器有很多不同的能力,它可以把抽象容器和通用算法有機(jī)的統(tǒng)一起來仲吏。

迭代器提供一些基本操作符:*不铆、++、==裹唆、!=誓斥、=。這些操作和C/C++“操作array元素”時的指針接口一致许帐。不同之處在于劳坑,迭代器是個所謂的復(fù)雜的指針,具有遍歷復(fù)雜數(shù)據(jù)結(jié)構(gòu)的能力成畦。其下層運(yùn)行機(jī)制取決于其所遍歷的數(shù)據(jù)結(jié)構(gòu)距芬。因此,每一種容器型都必須提供自己的迭代器循帐。事實(shí)上每一種容器都將其迭代器以嵌套的方式定義于內(nèi)部框仔。因此各種迭代器的接口相同,型號卻不同拄养。這直接導(dǎo)出了泛型程序設(shè)計(jì)的概念:所有操作行為都使用相同接口离斩,雖然它們的型別不同。(以上定義摘自百度百科)

下面我們來看一下迭代器的實(shí)現(xiàn):

template<class T>
struct __list_node
{
    void* prev;
    void* next;
    T data;
}
template<class T,class Ref,class Ptr>
struct __list_iterator
{
    typedef __list_iterator<T,Ref,Ptr> self;
    typedef Ptr pointer;
    typedef Ref reference;
    typeder __list_node<T>* link_type;
    link_type node;
    bool operator==(const self& x)const{return node==x.node;}
    bool operator!=(const self& x)const{return node!=x.node;}
    
    reference operator*()const{return (*node).data;}
    pointer operator->()const{return &(operator*());}
    
    self& operator++(){node=(link_type)((*node).next);return *this;}
    self operator++(int){self tmp=*this;++*this;return tmp;}
    self& operator--(){node=(link_type)((*node).prev);return *this;}
    self& operator--(int){self tmp=*this;--*this;return tmp;}
};

我們把其中隔開的兩個函數(shù)抽出瘪匿,簡單的分析一下跛梗。

從使用者的角度來講,只會通過右上角的方式來調(diào)用棋弥,然而實(shí)際的處理過程如左側(cè)所示:

  • 當(dāng)執(zhí)行*ite時,會獲得(*node).data;其中*node為一個object核偿,data為其中的成員。
  • 當(dāng)執(zhí)行ite->method()時顽染,會調(diào)用上方的operator*()獲得(*node).data,返回其地址漾岳。

這樣一來就完美的將原生指針node包裹在了迭代器__list_iterator中。

第五節(jié) function-like classes (仿函數(shù))

仿函數(shù)在標(biāo)準(zhǔn)庫中有著廣泛的應(yīng)用粉寞,這節(jié)課我們將從標(biāo)準(zhǔn)庫中抽取一個案例來探討仿函數(shù)的用法尼荆,對于為什么要讓一個類模仿函數(shù)行為,這節(jié)我們不做討論仁锯。

通常來講,如果一個東西可以接收小括號這種操作符我們就叫它函數(shù)翔悠,或者像函數(shù)的東西业崖。

上面是標(biāo)準(zhǔn)庫中的一段代碼(有省略)野芒。

select1st與select2st分別通過對()的重載提取pair對象的第一個元素和第二個元素。

圖片中灰色處省略了部分代碼双炕,展開如下:

再來看看標(biāo)準(zhǔn)庫中其它的仿函數(shù):

我們發(fā)現(xiàn)標(biāo)準(zhǔn)庫中的仿函數(shù)通常要繼承一些古怪的base山叮,下面是base的原型:

在這里我們不對base做任何討論总放,在后面有專門講解STL的課程會深入講解。

第六節(jié) namespace 經(jīng)驗(yàn)談

#include<iostream>

namespace lalala
{
    int a=5;
}
namespace lalala1
{
    int a=10;
}


int main()
{
    std::cout<<lalala::a<<std::endl;
    std::cout<<lalala1::a<<std::endl;
    return 0;
}

很小的話題,給出一段示例代碼贺纲,相信有一定C++基礎(chǔ)的人都可以理解。

第七節(jié) class template

template<typename T>
class complex
{
public:
    complex(T r=0,T i=0)
    :re(r),im(i)
    {}
    complex& operator +=(const complex&);
    T real () const { return re; }
    T imag () const { return im; }
private:
    T re,im;
    friend complex& _doapl(complex*,const complex&);
};
complex<double> c1(2.5,1.5);
complex<int> c2(2,6);
解析:

template的基本使用方法企锌,不做贅述角寸。

在定義object時指明模板類型,傳入后與符號T綁定死相。

PS:在template<...>的尖括號中融求,class與typename是等價(jià)的。

第八節(jié) Function Template(函數(shù)模板)

class stone
{
public:
    stone(int w,int h,int we)
    :_w(w),_h(h),_weight(we)
    {}
    bool operator< (const stone& rhs) const
    { return _weight < rhs._weight; }
private:
    int _w,_h,_weight;
}
template<class T>
inline
const T& min(const T& a,const T& b)
{
    return b<a?b:a;
}
stone r1(2,3),r2(3,3),r3;
r3=min(r1,r2);
解析:

stone兩個object r1算撮,r2生宛,傳入min函數(shù)。min函數(shù)在接收參數(shù)后將參數(shù)類型與模板類型T綁定(實(shí)參引導(dǎo))肮柜,確認(rèn)類型后a,b進(jìn)行'<'操作陷舅,編譯器會進(jìn)入T類(stone)類內(nèi)尋找對應(yīng)的重載函數(shù)來執(zhí)行。

PS:該用法在“(GeekBand)C++面向?qū)ο蟾呒壘幊蹋ㄉ希┑诙芄P記(1)”中有過介紹审洞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末莱睁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子预明,更是在濱河造成了極大的恐慌缩赛,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撰糠,死亡現(xiàn)場離奇詭異酥馍,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)阅酪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門旨袒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人术辐,你說我怎么就攤上這事砚尽。” “怎么了辉词?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵必孤,是天一觀的道長。 經(jīng)常有香客問我,道長敷搪,這世上最難降的妖魔是什么兴想? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮赡勘,結(jié)果婚禮上嫂便,老公的妹妹穿的比我還像新娘。我一直安慰自己闸与,他們只是感情好毙替,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著践樱,像睡著了一般厂画。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上映胁,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天木羹,我揣著相機(jī)與錄音,去河邊找鬼解孙。 笑死坑填,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弛姜。 我是一名探鬼主播脐瑰,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼廷臼!你這毒婦竟也來了苍在?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤荠商,失蹤者是張志新(化名)和其女友劉穎寂恬,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莱没,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡初肉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了饰躲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牙咏。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嘹裂,靈堂內(nèi)的尸體忽然破棺而出妄壶,到底是詐尸還是另有隱情,我是刑警寧澤寄狼,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布丁寄,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伊磺。R本人自食惡果不足惜宁舰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奢浑。 院中可真熱鬧,春花似錦腋腮、人聲如沸雀彼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽徊哑。三九已至,卻和暖如春聪富,著一層夾襖步出監(jiān)牢的瞬間莺丑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工墩蔓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梢莽,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓奸披,卻偏偏與公主長得像昏名,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子阵面,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容