(GeekBand)C++面向對象高級編程(下)第一周筆記(1)

第一節(jié) 導讀

知識清單:

首先來列一張清單,清點一下后面課程將會深入的細節(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)
目標:
  • 在先前的基礎課程所培養(yǎng)的正規(guī)、大氣的編程素養(yǎng)上,繼續(xù)探討更多技術。
  • 泛型編程(Generic Programming)和面向對象編程(Object-Oriented Programming)雖然分屬不同思維,但它們正是C++的技術主線,所以本課程也討論template(模板)。
  • 深入探索面向對象之繼承關系(inheritance)所形成的對象模型(Object Model),包括隱藏于底層的this指針,vptr(虛指針),vtbl(虛表),virtual mechanism(虛機制),以及虛函數(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(轉換構造與類型轉換函數)

為了方便對照學習,記憶,決定把二三節(jié)內容放在一起講解。

轉換構造函數

定義

在CPP中,類的構造函數可以省略不寫,這時CPP會為它自動創(chuàng)建一個隱式默認構造函數(implicit default constructor);也可以由用戶定義帶參數的構造函數,構造函數也是一個成員函數,他可以被重載;當一個構造函數只有一個參數,而且該參數又不是本類的const引用時,這種構造函數稱為轉換構造函數(non-explicit ont argument constructor)。(該段引自百度百科)

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

這個構造函數即 轉換構造函數。
如上文。構造函數只有一個參數 double x,它也不是本類的const引用。

應用

通過轉換構造函數可以將一個指定類型的數據轉換為類的對象。

1.用于定義

轉換構造函數一般由系統(tǒng)自動調用(當然代碼里自己調用完全沒問題),這點很利于編程。
例如:

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

這時系統(tǒng)就自動調用了 Complex(double x)將 5.0轉換成Complex類,再賦值給t。

2.用于計算

通常來講,轉換構造函數更多搭配運算符重載用來計算。

class Complex
{
public:
    Complex(double x,double y=0)//轉換構造
    :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;

編譯器會隱式調用轉換構造函數將5.0轉換為Complex成員并賦值給t。第二步同理,將4.8轉換成Complex成員后調用'+'的重載函數完成計算。

類型轉換函數

通過轉換構造函數可以將一個指定類型的數據轉換為類的對象。但是不能反過來將一個類的對象轉換為一個其他類型的數據(例如將一個Complex類對象轉換成double類型數據)。

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

operator double() const//類型轉換函數
{
    return real;
}

從函數結構來看,與重載函數類似,都需要關鍵字operator,只不過這里的轉換的是類型而已,double在Complex類中經過重載后,Complex就被賦予了一種新的含義,既可以當做Complex類型本身使用,也可以當做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運算有了另一種解法,即將Complex對象t通過隱式調用類型轉換函數轉換為double對象完成計算。

小結:

  • 轉換構造函數可以將一個指定類型的數據轉換為類的對象。
  • 類型轉換函數可以將一個類的對象轉換為一個其他類型的數據。

我們了解了轉換構造函數與類型轉換函數可以為Complex b=t+4.8這樣的運算提供兩個不同角度的解法,那么如果Complex類同時擁有了兩種函數,又會怎樣呢?

class Complex
{
public:
    Complex():real(0),imag(0)
    {}
    Complex(double x,double y=0)//轉換構造
    :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(歧義),即有多重解。當編譯器可選擇的方案不止一種,會出現(xiàn)這種提示。在案例中,編譯器既可以通過轉換構造函數將4.8轉換為Complex對象,與可以通過類型轉換函數將t轉換為double對象完成計算。

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

explicit關鍵字多用在轉換構造函數之前,其作用是指定該構造函數只能被顯示調用(即創(chuàng)建實例時的調用,如Complex a(5,0)),而不可以再被隱式調用,這樣就解決了程序的ambiguous問題。

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

我們知道智能指針能夠比原生指針做更多事情,例如處理線程安全,提供寫時復制,確保協(xié)議,并且提供遠程交互服務等等等等許許多多強大的功能。但其實不論是多牛的智能指針,在它的內部一定至少有一個原生指針在工作。現(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();
解析:

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

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

我們已經對操作符“*”和“->”進行了重載,當編譯器在執(zhí)行*sp時,返回值是*px,這很好理解,可是在執(zhí)行sp->時,返回值是sp,為什么能起到和sp->一樣的效果呢?

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


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

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

迭代器提供一些基本操作符:*、++、==、!=、=。這些操作和C/C++“操作array元素”時的指針接口一致。不同之處在于,迭代器是個所謂的復雜的指針,具有遍歷復雜數據結構的能力。其下層運行機制取決于其所遍歷的數據結構。因此,每一種容器型都必須提供自己的迭代器。事實上每一種容器都將其迭代器以嵌套的方式定義于內部。因此各種迭代器的接口相同,型號卻不同。這直接導出了泛型程序設計的概念:所有操作行為都使用相同接口,雖然它們的型別不同。(以上定義摘自百度百科)

下面我們來看一下迭代器的實現(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;}
};

我們把其中隔開的兩個函數抽出,簡單的分析一下。

從使用者的角度來講,只會通過右上角的方式來調用,然而實際的處理過程如左側所示:

  • 當執(zhí)行*ite時,會獲得(*node).data;其中*node為一個object,data為其中的成員。
  • 當執(zhí)行ite->method()時,會調用上方的operator*()獲得(*node).data,返回其地址。

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

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

仿函數在標準庫中有著廣泛的應用,這節(jié)課我們將從標準庫中抽取一個案例來探討仿函數的用法,對于為什么要讓一個類模仿函數行為,這節(jié)我們不做討論。

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

上面是標準庫中的一段代碼(有省略)。

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

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

再來看看標準庫中其它的仿函數:

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

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

第六節(jié) namespace 經驗談

#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++基礎的人都可以理解。

第七節(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é) Function Template(函數模板)

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函數。min函數在接收參數后將參數類型與模板類型T綁定(實參引導),確認類型后a,b進行'<'操作,編譯器會進入T類(stone)類內尋找對應的重載函數來執(zhí)行。

PS:該用法在“(GeekBand)C++面向對象高級編程(上)第二周筆記(1)”中有過介紹。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容