(Boolan) C++面向?qū)ο蟾呒?jí)編程(四)

之前幾次娇豫,對(duì)C++中的一些核心話題進(jìn)行了一些梳理耀找,主要都是集中在關(guān)于面向?qū)ο蟮乃枷敕矫妗N彝ㄟ^部分故事的思路厂汗,結(jié)合生活來理解了關(guān)于面向?qū)ο蟮囊恍﹩栴}委粉。如果需要可以回看我文章列表的中的文章。今天的部分比較零散娶桦,我的思考可能也有限贾节,很多部分我的思考也不夠,不足以用故事來涵蓋趟紊,但是既然是個(gè)故事狗氮双,那么還是來說一下,類型到底是干什么用的霎匈。
但是C++的內(nèi)容非常龐大,之前也僅僅是冰山的一角送爸,還有非常豐富的知識(shí)有待梳理铛嘱。
在梳理今天內(nèi)容的之前,我想先簡(jiǎn)單說明一下關(guān)于類型的概念袭厂,為什么計(jì)算機(jī)系統(tǒng)中需要設(shè)計(jì)變量類型呢墨吓?如果你熟悉JavaScript,那么在其中聲明變量直接使用var xxx就行了纹磺,而在Python中帖烘,甚至連具體的類型名稱都不用寫!豈不是快哉橄杨?秘症!省下了大量的力氣來討論變量類型照卦,如果在c++11 之前,auto關(guān)鍵字還不能用于自動(dòng)聲明變量類型的時(shí)候乡摹,C++對(duì)于變量類型的要求可謂是相當(dāng)?shù)膰?yán)格了役耕。
那么咱們就先來說說,為什么需要有變量類型這件事吧聪廉。

  • 變量類型的作用
    這個(gè)需要從非常原始的話題開始說起了~~~~原始到要存二進(jìn)制開始說起瞬痘。。板熊。
    其實(shí)我們都知道框全,計(jì)算機(jī),之所以叫做計(jì)算機(jī)干签,是因?yàn)樗荒芡ㄟ^計(jì)算數(shù)字(真的就是計(jì)算數(shù)字竣况,連數(shù)學(xué)都算不上。筒严。丹泉。),而且為了方便計(jì)算機(jī)系統(tǒng)設(shè)計(jì)和實(shí)現(xiàn)鸭蛙,采用的是我們基本上很難讀懂的二進(jìn)制來計(jì)算我們想要的結(jié)果的摹恨。二進(jìn)制很方便的表明了的狀態(tài)。但是娶视,對(duì)于二進(jìn)制來說晒哄,我們基本沒辦法直接讀懂到底其中在說啥(就算能讀懂,也記不住肪获,可以參考一個(gè)智力比賽記燈泡的開關(guān)(個(gè)人覺得這個(gè)比賽喪心病狂寝凌,在此不吐槽了)。孝赫。较木。。)青柄。他的最根本的優(yōu)點(diǎn)也十分突出伐债,** 那就是簡(jiǎn)單(簡(jiǎn)單到只有兩個(gè)數(shù)) 但隨之而來的缺點(diǎn)也很明顯,那就是他實(shí)在太長(zhǎng)了致开。比如一個(gè)很簡(jiǎn)單的236使用二進(jìn)制表示卻成了1110 1100(幸好我還很用心的添加了空格<實(shí)際怕自己寫錯(cuò)被打臉>)峰锁,而這些我們很難直接讀懂的東西會(huì)連續(xù)的寫在一起,像0011 1001 0110 1100 1010這樣双戳,那么我們就很那區(qū)分出來虹蒋,這到底是我們?nèi)粘V惺煜さ?code>235210還是57 和 869等等等等。因?yàn)檫@牽扯到我上篇文章講的那個(gè)鑰匙和箱子的故事**,也就是我們并不能通過數(shù)字來判斷魄衅,多少個(gè)單位能夠算作一組峭竣,這一組能夠表達(dá)一個(gè)想要的數(shù)字。那這個(gè)時(shí)候徐绑,再拿出之前的故事邪驮,想知道箱子里面放的啥,那在箱子上貼上名字就行啦傲茄。其實(shí)類型相當(dāng)于這個(gè)標(biāo)簽毅访,在連續(xù)的二進(jìn)制數(shù)字的序列中,就可以輕松知道盘榨,這個(gè)變量占有多大的內(nèi)存喻粹。
    因此,類型聲明草巡,其實(shí)是為了告訴編一起守呜,我這個(gè)變量需要多大的內(nèi)存空間,比如int一般為4個(gè)字節(jié)山憨,編譯器可以非常方便的按照大小取出全部的數(shù)據(jù)來了查乒。那么為什么C/C++對(duì)于內(nèi)存這么關(guān)注,而Python這類語言不關(guān)注呢郁竟?其實(shí)玛迄,對(duì)于python這類解釋形語言來說,在他和內(nèi)存之間其實(shí)還有一個(gè)東西叫做虛擬機(jī)棚亩,他主要負(fù)責(zé)控制變量的類型計(jì)算蓖议,而不需要Python親力親為來控制變量的類型了。(其實(shí)也就是把類型控制的部分抽出來了讥蟆,不代表他沒有類型勒虾,不然Python中不會(huì)有type()內(nèi)置函數(shù)了)
    那么C/C++的類型如此重要,會(huì)不會(huì)牽扯到一個(gè)問題呢瘸彤,那就是類型轉(zhuǎn)換的問題了修然。

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

  • 轉(zhuǎn)為其它類型(Conversion function)
//定義
class Fraction
{
public:
    Fraction(int num, int den = 1) :m_numerator(num), m_denminator(den){}
    //定義轉(zhuǎn)換函數(shù)
    //可以告訴編譯器,可以將Fraction轉(zhuǎn)換為double
    operator deouble() const 
    { 
        if(m_denominator)
            return (double) (m_numerator / m_denominator) ; 
       else
            return 1.0;
    }
private:
    int m_numerator;
    int m_denominator;
};
//使用
Fraction f(3, 5);
double d = 4 + f;  //調(diào)用operator double() 將f轉(zhuǎn)為0.6钧栖,再進(jìn)行運(yùn)算
//編譯器會(huì)先查看是否存在`double operator+(int, Fraction)`的函數(shù)聲明低零,如果該函數(shù)存在,則使用操作符重載
//如果操作符重載不存在拯杠,編譯器會(huì)查找是否存在Fraction想double的轉(zhuǎn)換函數(shù)
  • 語法:

    operator targetType() const
    { 
      return statement; 
    }
    
  • 轉(zhuǎn)換函數(shù)的功能

    • 可以通過該函數(shù)告訴編譯器,可以將自己轉(zhuǎn)換為某一個(gè)類型
  • 轉(zhuǎn)換函數(shù)的注意事項(xiàng)

    • 不需要參數(shù)
    • 不需要返回類型
    • 返回值需要考慮是否為const的問題
  • 其它類型轉(zhuǎn)為自己的類型(non-explicit-one-argument constructor)

//定義
class Fraction
{
public:
    //non-explicit-one-argument constructor
    //構(gòu)造函數(shù)中部分有默認(rèn)值啃奴,構(gòu)造時(shí)潭陪,不需要傳遞全部參數(shù),只要一個(gè)實(shí)參就可以
    Fraction(int num, int den = 1)
    : m_numerator(num), m_denominator(den) {}
    Fraction operator+(const Fraction& f)
    {
        return Fraction(....);
    }
private:
    int m_numerator;
    int m_denominator;
};
//調(diào)用
Fraction f(3, 5);
Fraction d2 = f + 4;//調(diào)用non-explicit ctor 將4轉(zhuǎn)為Fraction,再調(diào)用operator+(該函數(shù)按照上方定義得知依溯,需要Fraction+Fraction)
  • 語法

    • 按照正常構(gòu)造方法老厌,但在形參列表處,需要對(duì)不需要的參數(shù)寫入默認(rèn)值黎炉,最后只留下一個(gè)形參需要外部傳入即可構(gòu)造對(duì)象枝秤,該構(gòu)造函數(shù),可以將其他類型轉(zhuǎn)化為需要的類型
  • explicit關(guān)鍵字

    • 提出問題
//定義
class Fraction
{
public:
      //non-explicit-one-argument constructor
      //構(gòu)造函數(shù)中部分有默認(rèn)值,構(gòu)造時(shí),不需要傳遞全部參數(shù)逻炊,只要一個(gè)實(shí)參就可以
      Fraction(int num, int den = 1)
      : m_numerator(num), m_denominator(den) {}
      Fraction operator+(const Fraction& f)
      {
          return Fraction(....);
      }
      operator double() const 
      {
         if(m_denominator)
            return (double) (m_numerator / m_denominator) ; 
         else
            return 1.0;
      }
private:
      int m_numerator;
      int m_denominator;
};
//調(diào)用
Fraction f(3, 5);
Fraction d2 = f + 4; // [ERROR]Ambiguous
//符合語法泊碑,但是此處存在二義性,運(yùn)行時(shí)異常
//語義一:轉(zhuǎn)換函數(shù)厌均,編譯器可以將f轉(zhuǎn)換為double,再執(zhí)行加法運(yùn)算
//語義二:存在non-explicit-one-argument constructor,所以編譯器也可以將常數(shù)4構(gòu)造為Fraction沐序,再進(jìn)行加法運(yùn)算。
//由于以上兩種方案堕绩,都可以實(shí)現(xiàn)該條語句的調(diào)用策幼,所以編譯器產(chǎn)生二義性,不能選擇奴紧,隨報(bào)錯(cuò)特姐。
  • explicit的用法
    • 僅使用在構(gòu)造函數(shù)處

    • 告訴編譯器,該構(gòu)造函數(shù)绰寞,僅用于調(diào)用到逊,不能用于轉(zhuǎn)換構(gòu)造使用

      explicit Fraction(int num, int den = 1)
      : m_numerator(num), m_denominator(den) {  }
      

智能指針(pointer-like classes)

//定義
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(void){....}
};
shared_ptr<Foo> sp(new Foo);
Foo f(*sp);
sp->method();//實(shí)際通過智能指針返回了Foo的指針
//讓對(duì)象sp,也具有了指針一樣的使用方法
//關(guān)于->符號(hào)滤钱,有一個(gè)特殊行為觉壶,當(dāng)作用下去后,得到對(duì)象后件缸,不會(huì)被消耗掉
  • 智能指針中一定持有一個(gè)真正的指針

仿函數(shù)(function-like classes):重載()操作符

/定義部分
template <class T>
struct identity
{
    const T&;
    operator() (const T& x) const { return x; }
};
template <class Pair>
struct select1st
{
    const typename Pair::first_type& operator()(const Pair& x) const 
    {
        return x.first;
    }
};
template <class Pair>
struct select2nd
{
    const typename Pair::second_type& operator()(const Pair& x) const 
    {
        return x.second;
    }
}
//pair部分
template <class T1, class T2>
struct pair
{
    T1 first;
    T2 second;
    pair(): first(T1()), second(T2()){}
    pair(const T1& a, const T2& b)
      :first(a), second(b){}
}
//使用部分
select1st <pair>() ();
//第一個(gè)括號(hào):創(chuàng)建對(duì)象
//第二個(gè)括號(hào):調(diào)用操作符()重載

namespace

  • 通過namespace可以將部分內(nèi)容區(qū)分開铜靶,不需要考慮多人協(xié)作時(shí),類他炊、函數(shù)名稱沖突的情況争剿,只需要自己將自己的代碼用namespace包起來即可
.....
//定義namespace
namespace storyDog
{
     ......
    void function1(){....}
     ......
}
.....
......
//使用
storyDog::function1();
......

模版

  • 類模版(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;
}
{
//使用的時(shí)候指定T的類型
complex<double> c1(2.5, 1.5);
complex<int> c2(c2, 6);
}
  • 函數(shù)模版(function template)
//定義
template <class T>
inline const T& min(const T& a, const T& b)
{
      return b < a? b: a;
}
class stone
{
public:
    stone(int w, int h, int weight):_w(w), _h(h), _weight(weight){}
    bool operator< (const stont rhs) const
    {
        return _weight < rhs._weight;
    }
private:
    int _w, _h, _weight;
}
//使用
stone r1(2, 3), r2(3, 3);
r3 = min(r1, r2);
//編譯器會(huì)對(duì)function template進(jìn)行實(shí)參推倒(argument deduction),無需手動(dòng)指定具體類型是什么痊末。
  • 成員模版(member template)
template <class T1, class T2>
struct pair{
    typedef T1 first_type;
    typedef T2 second_type;
    T1 first;
    T2 second;
    pair():first(T1()), second(T2()){}
    pair(const T1& a, const T2& b):first(a),
 second(b){}
    //成員模版(在模版中的模版)
    //可以更細(xì)微的使用模版蚕苇,不需要與類模版相同
    template<class U1, class U2>
    pair(const pair<U1, U2>& p):first(p.first), second(p. second){}
}
  • 考慮帶有子父類模版的情況
template<typename _Tp>
class share_ptr:public __shared_ptr<_Tp>
{
    ...
    template<typename _Tp1>
    explicit shared_ptr(_Tp1* __p)
        : __shared_ptr<_Tp>(__p){}
    ...
  };
  ```

    ```
    Base1* ptr = new Derived1; //up-cast
    shared_ptr<Base1> sptr(new Derived1);  //模版up-cast
    ```
- 模版特化(Specialization)

//泛化的版本
template<class key>
struct hash();

//特化的版本
template<>
struct hash<long>{
size_t operator()(long x) const { return x;}
};

cout << hash<long>()(1000);

- 模版的偏特化(partial specialization)
- 個(gè)數(shù)上的偏特化

template<typename T, typename Alloc=...>
class vector
{
....
}
....
template <typename Alloc = ....>
//綁定了其中的一個(gè),另外一個(gè)沒有被特化
class vector<bool, Alloc>
{
....
}

  - 范圍的偏特化

//非指針用這套代碼
template<typename T>
class C{....};
//指針用這套代碼
template <typename T>
class C<T*>{....};
```

  • 模版模版參數(shù)(template template parameter)
//定義
template<typename T,
       template <typename T> 
       class Container>
//含義:第二個(gè)模版接受一個(gè)Container
//并且凿叠,這個(gè)Container的模版接受第一模版參數(shù)作為他的模版參數(shù)
class XCls
{
private:
     Container<T> c;
public:
      ....
};
//使用
template<typename T>
using Lst = list<T, allocator<T>>;
XCls<string, Lst> mylst;

淺談C++11

  • 可變數(shù)量的模版參數(shù)(variadic templates)
void print(){}
//定義
template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args) 
{
    cout << firstArg << endl;
    print(args...);//遞歸調(diào)用涩笤,來解析模版參數(shù)包
}
.......
{
      //調(diào)用
    print(7.5, "hello", bitset<16>(377), 42);
//想要知道后續(xù)的參數(shù)包的大小嚼吞,可以使用sizeof...(args)來獲得
}
  • auto關(guān)鍵字
list<string> c;
auto ite = find(c.begin(), e.end(), target);
//通過auto來聲明變量類型
//原始的是list<string>::iterator ite;現(xiàn)在簡(jiǎn)化為auto即可。

注意:使用auto聲明變量蹬碧,必須初始化舱禽,否則會(huì)報(bào)錯(cuò)。

  • ranged-base for
    • 對(duì)于可以遍歷的對(duì)象恩沽,可以簡(jiǎn)化的來寫for循環(huán)
for(decl :coll){
        statement
}
  • pass by value(不會(huì)影響原始容器的值)
vector<double> vec;
for(auto elem: vec){
    cout << elem << endl;
 }
  • pass by reference(可以改變?cè)既萜鞯膬?nèi)容)
vector<double> vec;
for(auto& elem: vec){
    elem += 3;//可以改變?cè)萜鞯膬?nèi)容
 }

reference

引用(reference):就是對(duì)象的另一個(gè)名字誊稚。(名字是名詞,所以此時(shí)我們把引用當(dāng)做一個(gè)名詞)罗心。引用主要用作函數(shù)的形式參數(shù)里伯。(作為參數(shù)的,那更是名詞了)协屡。到此為止俏脊,接下來就好理解了,因?yàn)樗莻€(gè)名詞肤晓,對(duì)于名詞的理解就比動(dòng)詞的理解方便多了爷贫。
進(jìn)一步理解:引用是一種復(fù)合類型(引用又是一種類型),通過在變量名前添加”&“符號(hào)來定義补憾。復(fù)合類型指的是用其他類型來定義的類型漫萄。
結(jié)論:其實(shí)引用只是一個(gè)別名,即只是他綁定的對(duì)象的另一個(gè)名字盈匾,作用在引用上的所有操作事實(shí)上都是作用在該引用綁定的對(duì)象上

關(guān)于面向?qū)ο蟮膯栴}

關(guān)于這個(gè)問題啊腾务,我之前的筆記寫的很詳細(xì),在這里也不想再贅述了削饵。鏈接:
http://www.reibang.com/p/34a30505176d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末岩瘦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子窿撬,更是在濱河造成了極大的恐慌启昧,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劈伴,死亡現(xiàn)場(chǎng)離奇詭異密末,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)跛璧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門严里,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人追城,你說我怎么就攤上這事刹碾。” “怎么了座柱?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵教硫,是天一觀的道長(zhǎng)叨吮。 經(jīng)常有香客問我辆布,道長(zhǎng)瞬矩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任锋玲,我火速辦了婚禮景用,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惭蹂。我一直安慰自己伞插,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布盾碗。 她就那樣靜靜地躺著媚污,像睡著了一般。 火紅的嫁衣襯著肌膚如雪廷雅。 梳的紋絲不亂的頭發(fā)上耗美,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音航缀,去河邊找鬼商架。 笑死,一個(gè)胖子當(dāng)著我的面吹牛芥玉,可吹牛的內(nèi)容都是我干的蛇摸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼灿巧,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼赶袄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抠藕,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤饿肺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后幢痘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唬格,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年颜说,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了购岗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡门粪,死狀恐怖喊积,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情玄妈,我是刑警寧澤乾吻,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布髓梅,位于F島的核電站,受9級(jí)特大地震影響绎签,放射性物質(zhì)發(fā)生泄漏枯饿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一诡必、第九天 我趴在偏房一處隱蔽的房頂上張望奢方。 院中可真熱鬧,春花似錦爸舒、人聲如沸蟋字。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹊奖。三九已至,卻和暖如春涂炎,著一層夾襖步出監(jiān)牢的瞬間忠聚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國打工璧尸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咒林,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓爷光,卻偏偏與公主長(zhǎng)得像垫竞,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蛀序,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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