C++筆記(2):《資料結構和問題求解》part1筆記

來源:互聯網
上載者:User

 

  前言:

  C++,資料結構,演算法,這些知識在互連網行業求職過程中是必備的,而本科電路硬體出身的我這些基本就沒學過,也用得比較少,為了以後的飯碗,從現在開始還是花點時間來看下這些東西吧。本節是mark allen Weiss資料結構書籍data structures and algorithm analysis in c++ (second edition)中第一大部分的筆記(隨手寫的,供自己以後參考),這部分主要是講解一些C++的知識,比如物件導向概念,繼承,多態,重載,虛函數,模板,設計模式等等。

 

  Chap1:

  本章主要是講一些array,string,struct,pointer的東西,屬於c++的一些非常基本的資料類型。

  64bit Win7+32bit vs2010經過測試,發現不同資料類型佔用的記憶體大小情況如下:

  unsigned char:1, char:1, unsigned int:4, int:4, long:4,unsigned long:4

  32bit的系統只能說明變數的地址是32bit(即4位元組)的,與地址裡面的內容所佔記憶體大小無關。

  一個指標也被看成是一個指向object的object。

  array裡面儲存的是相同資料類型的變數,這些變數可以是結構體,而結構體中又可以用不同的資料類型。

  vector代替原始的array,而string代替原始的strings。

  其中vector和string屬於一流的object,因為它們滿足通用的操作。稍微不同的是vector是個類模板,而string是個類。

  vector使用數組下標訪問時不會進行邊界檢查,為了保險,需要保證它訪問的範圍在它邊界內,所以此時的用法和普通的array沒區別。

  參數傳遞過程中採用引用而不是直接的形參一個原因是可以減小copy實參帶來的開銷。不過直接用引用的話有可能改變原來變數的內容,所以一般情況下都是用常量引用(如果不想原來的參數被改變的話)。

  在STL中沒有對應多維陣列的object,不過在OpenCV中的Mat貌似有這個功能,二維的。

  stirng中的c_str指返回一個char*類型,它指向的內容和原來的string一樣。

  對指標變數進行declaration操作時必須保證該指標已經正確初始化過了。

  自動記憶體配置和動態記憶體分配是2個不同的概念,自動指的是程式自動執行,而動態指的是手動執行,在需要使用和銷毀的時候聲明,對應的關鍵字為new, delete. 如果pt是個指標,執行delete pt只是把pt指向的那一塊記憶體給收回了,而pt本身這個變數還是存在的。

  使用指標傳遞時屬於c語言風格,而用引用傳遞時為c++語言風格。引用傳遞時,其實內部已經隱式將地址傳遞進去了,系統會自動每時每刻的解引用。使用引用的好處是我們只需在聲明和定義引用時加入符號”&”,在函數內部實現時不要額外加其它符合,直接採用實值變數即可。在調用函數時也無需加入其它等符號。

  因為struct中變數的資料類型不完全相同,所以我們不能像操作array那樣迴圈操作它的每個變數。Struct裡麵包含多個變數類型,作為參數傳遞時一般不採用實值傳遞,因為這樣的開銷太大(主要是參數複製方面)。

  如果ps是指向結構體的指標,a為結構體的一個成員變數,則(*ps).a表示訪問結構體中的變數a,但是這樣看起來比較彆扭,所以在c++中提供了一個符號”->”帶代替,此時的表達為ps->a.

 

  Chap2:

  類中定義的操作類型不能超過c++內嵌的資料操作類型範圍。

  Object是一個原子單元,不可分割的。類的性質如下:資訊隱藏性,封裝性,代碼複用性,模板機制,繼承機制,多態性(作為繼承性的一部分實現)。

  Object-oriented programming:物件導向編程。

  Object-based programming:基於對象編程,該思想也有封裝,資訊隱藏,代碼複用等機制,但是不包括多態和繼承性質。

  因為在執行類的建構函式時是先依次進行變數的所有記憶體配置,然後才進行變數的賦初值。一般類的資料成員都有其預設的賦值機制,如果需要更改其預設的值,可以在建構函式體中實現。但是有些變數,比如const變數,它需要在定義它(即分配記憶體)的時候給它賦初值,但是代價函數是首先分配完所有的記憶體,然後再賦值,明顯不符合const變數初始化的要求。因此const變數的初始化只能在建構函式後的冒號初始化列表中進行,該處的優先順序比建構函式本體的還要高。另一個例子就是類中有類的資料成員,並且它沒有預設的初始化函數,因此這個類的初始化也應該在初始化列表中進行。

  關鍵字explicit一般在建構函式前使用,且一般是單參數的建構函式,因為在C++中,如果一個類的建構函式是單參數的,它是允許直接給一個類的對象賦數值的,這實際上是系統首先定義了一個類的臨時對象,並用該數值作為類的建構函式參數,然後將這個臨時對象賦值給新的類對象。如果使用了explicit關鍵字後,系統(編譯器)就不再允許直接將數字賦值給類對象了。

  const主要用在3個地方:函數參數傳遞時,函數傳回值,函數參數括弧後。

  在類中,解構函式,copy建構函式,賦值操作符,稱為big three,這3個函數在系統內部已經有了個預設的實現,一般情況下我們不需要更改它。

  解構函式主要是釋放類中的變數,這些變數包括建構函式中定義的以及其他成員函數定義的,還有就是那些在類中使用new後的所有變數,它也關閉那些開啟了的的所有文檔。

*this表示當前的對象。

  當類的資料成員有指標變數時,一般情況下需要重寫Big Three。其中的解構函式需要用delete收回對應的指標變數。Copy建構函式需要滿足deep copy操作。

  將需要初始化特定值的變數盡量放在初始化列表中初始化,這樣避免額外的賦值開銷,因為那些不再初始化列表的變數的初始化過程都是在0-參數的建構函式中進行初始化的,如果你在自己重載的建構函式體中重新進行初始化,就相當於了2次賦值,並且有可能引起一些記憶體問題(上面的理解可能有問題)。資料成員的初始化順序不是依照初始化列表進行的,而是依照在類中聲明的次序進行的,因此我們要盡量避免定義那些相互依賴的變數。

  下面的情況下資料成員一定要在初始化列表中進行初始化:常量;引用變數;沒有0-參數建構函式的變數。

  C函數中常犯的一個錯誤是返回一個指標(函數內部的)給一個動態變數,c++函數中常犯的一個錯誤是返回一個引用給動態變數。

  在c++中,下面5個操作符是不能重載的:”.”, ”.*”, ”?:”, ”::”, ”sizeof”.並且操作符重載時只能重載c++中現有的操作符,不能重載新的操作符,且重載時其參數的個數不能夠改變。總的來說重載操作符不能改變原先操作符的文法和優先順序。

  本來由於類的資訊隱藏特性,其私人變數是不能被類對象調用的,它只能被內部的成員函數調用,但是如果有一個函數是該類的友元函數(一般情況下該函數不是該類的成員函數,而是外部函數),則在該函數內部是可以調用類中所有成員的。實際使用方式如下:當類A的對象B作為一個參數傳入到函數f(A的友元含稅)中時,在函數f的內部可以調用B中的所有成員,包括它的private成員(正常情況下,其對象時不能調用私人成員的)。

  一般情況下盡量少用友元函數,可以考慮使用全域函數,類靜態成員變數,類枚舉變數等代替友元函數。

  錯誤處理是c++類的設計中是個難題,一般情況下採用異常處理模式。異常處理常用的是try-catch語句,try有測試的意思,因為在try中實現的代碼有可能有常識性的錯誤,自己在try語句裡添加那些不符合邏輯的錯誤並throw一個整數值(其它類型的值也可以),這個整數可以理解為錯誤代號。然後在try語句後直接(不用分號)接catch函數,這個函數帶有參數,其參數為可以為任何資料類型,如果不知道是什麼類型,則可以採用通用符號“…”,然後在catch語句中實現對應的錯誤處理程式。

  系統自動調用new[]一般發生在建構函式,賦值操作符=,+=中,系統自定調用delete[]一般發生在解構函式中。

  函數值返回一個引用就是返回一個別名,返回一個常量引用就是表面返回的這個別名(所代表的值)不能被修改。

  操作符重載時的函數名為operator+,operator-等,即operator和操作符一起構成的。

  copy constructor與operator=的不同之處在於,在使用operator=時需要賦值符號兩邊的object都已經建立了,它是一個個元素進行賦值的。而copy constructor機制主要用於建構函式的copy,函數參數以value傳遞,函數傳回值也為value的情況。

 

  Chap3:

  類模板的出現主要是為了代碼的重複利用。一般是針對類型獨立的場合。

  模板分為函數模板和類模板。模板中實現的一般是通用的演算法,簡單的模板思想是:用typedef定義一個通用類型,然後用通用類型來實現函數內部。

  在使用函數模板時,需要在使用前進行目標的聲明,一般情況下如下所示:template<class anyType>

  其中聲明過程用角括弧,且後面不需要加分號,直接接函數或者類的實現過程,其中的anyType表示模板中可以識別的類型等,且目標中類別的個數一般不會太多,1到2個是最常見的。

  使用類模板的基本原理和函數模板相同,但是有3個地方需要注意:1. 在類的實現前也需加入上面的模板聲明語句,且在類的外面實作類別中中的成員函數時,如果用到了類模板參數anyType,則每個函數實現前都需要加入類模板聲明。2. 在類外面實作類別中的成員函數,除了前面要加入類模板聲明語句外還需要在類的網域作業符”::”前也加入類模板參數<anyType>. 3. 在用帶模板的類初始化一個對象時,需要給類模板參數一個確定的參數,也是用角括弧括起來。

  類模板的目的是為了實現一個通用的類,這樣當不同的類型傳入時都可以完成其想要的功能,體現了代碼的重用性。不過還有一種模板叫做特殊模板,即它只適用於某個特定的類型,它的類模板聲明語句為:template<>,裡面是空的。這種使用方法一般是跟在一個通用類模板後面,因為它的模板參數為空白,所以在類的實現時其模板要傳遞具體的類型。這樣當建立一個目標類時,如果它屬於哪個具體的類型(即特殊模板),則按照特殊模板來處理,否則就屬於通用模板了。

  在老版本的類模板的實現代碼時,類模板的實現也是放在.h檔案中的,因為當時分開編譯會出現一些問題,且在對應的cpp檔案在實現每個函數之前需要加入類模板聲明。

  類模板有預設的參數,也可以由多個參數。

 

  Chap4:

  類之間常見的關係有IS-A,HAS-A。其中類的繼承屬於IS-A。

  指向基類的指標或者引用是可以指向其子類對象。

  公有繼承時父類的隱藏屬性在子類中是不變的,私人繼承時,父類中所有的成員在子類中都是私人的。即使是公有繼承,其父類的建構函式和解構函式是不能夠被子類所繼承的,但是在子類進行對象化時,由於用到了父類,所以其父類必須先建立,這時候父類的建構函式也會被使用,但子類銷毀時,先執行的是子類的解構函式,然後才執行父類的解構函式。

  當把父類中的任何一個成員放在了子類的private區時,表示這些成員在子類中是不能再被使用的,如果將其放在public區時,就需要覆蓋重新實現這些成員。

  類的protected成員只對它自己和它的子類開放,對其它類不開放。

  子類建構函式的初始化列表中可以直接通過函數名調用基類的建構函式,如果此時子類的建構函式體內容為空白,則表明直接使用父類的建構函式。

  子類的設計一般不需要再考慮父類的那些私人成員,因此父類中私人成員的更改對子類的設計毫無影響。

  重寫分為2種,一種是直接重寫成員函數,一種是重寫虛函數,但是只有重寫虛函數才是體現類的多態性。多態性的目的是為了實現介面重用。

  虛函數有一個好處是可以直接用父類的指標調用對應的子類的函數,而不需要用子類對象去調用。非純虛函數的情況下,如果子類沒有實現對應的虛函數代碼,則子類對象調用的是父類的那個函數內容。

  要真正實現多態(採用指標或引用訪問時),應該將virtual關鍵字放在父類中聲明,放在子類中是無效的。

  即使在父類中,建構函式,解構函式,賦值操作,拷貝建構函式等放在public區,且子類也是public方式繼承父類的,如果子類中沒有重新定義自己的這幾個函數,那麼系統會自動為子類這幾個函數採用預設的方法構造,而不是用父類的,因為子類不繼承這些。

  在類的設計中,通常情況下是將建構函式定義為非虛的,而將解構函式定義為虛函數。

  含有至少一個抽象方法(一般叫做純虛函數,後接”=0”來代替函數體)的類叫做抽象類別,抽象類別是不能用來執行個體化對象的,它的子類如果沒有實現對應的抽象方法則也不能用來執行個體化對象,因此它的建構函式平常是用不到的,除非子類調用來初始化父類的一些變數。不過可以定義指向抽象類別的指標或者引用來指向它的子類(非抽象的子類)。

  類中函數的預設參數都是靜態繫結的,一般情況下其它的成員函數(虛函數)地址也是靜態繫結的。

  函數重載:在一個類中,函數與函數的函數名相同,參數類型或者參數的個數不同,對函數的傳回值不一定需要完全相同,只需相容即可。

  函數覆蓋:在基類和衍生類別中,基類的函數必須是虛函數,兩個類中的函數與函數的函數名相同,參數類型和個數也完全相同,函數的傳回值類型也要求相同。

  函數隱藏:在基類和衍生類別中,兩個類中的函數名相同情況下,不滿足函數覆蓋的所有情況都是函數隱藏。

  公有繼承體現的是IS-A關係,而私人繼承體現的是HAS-A關係。一般情況下盡量少使用私人繼承方式。並且預設情況下子類的繼承都是私人繼承,所以在子類繼承為非私人繼承時不要忘了添加對應的關鍵字。

  父類的友函數原則上不是子類的友函數,不過父類的友函數可以訪問子類中從父類公用繼承的那些成員。

  如果要體現類的多態性機制,則一般情況下不要將對象當做值傳遞。

  類應用中的composition機制指的是一個類代碼的實現過程中會調用另一個類對象,最常見的情況就是將一個類對象作為參數傳遞到另一個類的建構函式中。

 

  Chap5:

  一個設計模式類似於一個菜譜,它有模式的名字,問題的描述,問題的解決方案和結果描述。

  如果我們要實現在一個比較大小的函數中,其參數要滿足各種資料類型,我們可以在每個對應的類中實現比較操作符”<”,這樣每實現一個類就需要重載一次操作符。假設我們不希望在類的內部實現該功能,就應該在類外部實現該操作符,但是一旦這個操作符函數定了,就只能有一個並不能滿足所有的類型的情況了。所以另外一種實現方法就是在類的外面實現多個比較函數,這個函數就叫做functor。Functor的大致的意思就是把一個類對象當函數一樣使用,當然這個類需要重載()操作符。因為他比較像函數,所以得了一個叫Funtor的名字,它來自於commond設計模式。一個functor僅僅包含一個方法,沒有包含資料成員,所以將它當為參數傳遞時需要以值傳遞的方式進行。Functor也就是我們常說的函數控制代碼,可以用類來實現。這樣傳入完成比較功能的函數的參數為資料集以及這個函數類了。

  C++標準中有一個wrapper指標類名為auto_ptr,這個指標能夠自動的動態刪除記憶體。一般指標記憶體的刪除發生在下面3種情況:1. 函數內部new的局部變數。 2. 函數內部new的局部變數指標且作為傳回值,該返回指標在另外一個函數中被調用,當調用它的函數結束時,需要釋放new過的記憶體。 3. New後的一個指標作為參數傳入一個函數中,而在這個函數的內部使用了delete掉這個參數指標。auto_ptr的內部有一個指標以及一個owner的指示變數,如果是這個指標的”主人”, 則解構函式時需要刪除對應的指標記憶體,如果複製操作發生,則轉移”主人”的身份。因此auto_ptr屬於一個指標wrapper。常量引用wrapper主要是用來處理引用指向NULL的情況。

  wrapper側重於封裝,而adapter側重於介面之間的轉換。

  為瞭解決類型雙向轉換帶來的編譯問題,我們在類的建構函式中盡量使用explicit關鍵字。

  指標可以指向一個目標或Null 物件,而引用只能指向一個目標,不能為NULL。

  當兩個類之間相互調用時,需要一個不完全的類聲明。

  Iterator模式是用來解決一個彙總對象的遍曆問題,將對彙總的遍曆封裝到一個類中進行,這樣就避免了暴露這個彙總對象的內部表示的可能。最直接的想法是遍曆一個容器,而不用考慮該容器內部的元素類型。

  迭代器的設計需要滿足自己有hasNext和next方法,並且對應的容器有getIterator的類型轉換方法。

  factory模式出現的原因:1. 一個基類指標可以指向任意它的子類對象,但是每次都需要用new來建立子類,且需知道子類的名字,這樣程式的擴充和維護會變得麻煩。2. 當類A中要用到基類B,且需要初始化B的一個子類,但是此時A並不知道該初始化哪個子類,不過A的子類知道。因此Factory提供了2個重要的功能:定義建立了類的介面,封裝了對象的建立;使得具體類的工作延遲到了子類當中。在factory模式中,每個容器都可以返回一個指向抽象類別的迭代器。

  Composite模式主要是當一個函數需要返回多個參數時(2個參數的最為常見)可以採用,比如說pair,pair常用於構造map和dictionary。

  Observer模式主要是解決一對多的依賴關係,當”一”(subject)變化了,則對應的多個依賴(obverser)也相應變化。常見例子就是excel圖表中一組資料可以有多個可視化結果,當其中資料改變了時,則這些可視化結果也跟著改變。一般情況下obverser有更新的功能。

 

  參考資料:

  data structures and algorithm analysis in c++ (second edition),mark allen Weiss.

  23種設計模式(C++)

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.