C++小結

1、構造函數和析構函數linux

對象的構造從類層次的最根處開始,在每一層中,首先調用基類的構造函數,而後調用成員對象的構造函數。析構則嚴格按照與構造相反的次序執行,該次序是惟一的,不然編譯器將沒法自動執行析構過程。安全

 一個有趣的現象是,成員對象初始化的次序徹底不受它們在初始化表中次序的影響, 只由成員對象在類中聲明的次序決定。這是由於類的聲明是惟一的,而類的構造函數能夠有多個,所以會有多個不一樣次序的初始化表。若是成員對象按照初始化表的次序進行構造,這將致使析構函數沒法獲得惟一的逆序。函數

最好把基類的析構函數聲明爲虛函數。這將使全部派生類的析構函數自動成爲虛函數。這樣,若是程序中顯式地用了delete運算符準備刪除一個對象,而delete運算符的操做對象用了指向派生類對象的基類指針,則系統會調用相應類的析構函數。ui

2、虛繼承this

虛繼承是多重繼承中特有的概念。虛基類是爲解決多重繼承而出現的。如:類D繼承自類B一、B2,而類B一、B2都繼承自類A,所以在類D中兩次出現類A中的變量和函數。爲了節省內存空間,能夠將B一、B2對A的繼承定義爲虛繼承,而A就成了虛基類。指針

class A對象

class B1:public virtual A;繼承

class B2:public virtual A;內存

class D:public B1,public B2;資源

3、多態

一、存在虛函數的類都有一個一維的虛函數表叫作虛表。類的對象有一個指向虛表開始的虛指針(指針在64位機器上佔8個字節)。虛表是和類對應的,虛表指針是和對象對應的。

二、在構造函數中進行虛表的建立和虛表指針的初始化。

三、虛表能夠繼承,若是子類沒有重寫虛函數,那麼子類虛表中仍然會有該函數的地址,只不過這個地址指向的是基類的虛函數實現。若是基類有3個虛函數,那麼基類的虛表中就有三項(虛函數地址),派生類也會有虛表,至少有三項,若是重寫了相應的虛函數,那麼     虛表中的地址就會改變,指向自身的虛函數實現。若是派生類有本身的虛函數,那麼虛表中就會添加該項。
四、派生類的虛表中虛函數地址的排列順序和基類的虛表中虛函數地址排列順序相同。

五、虛表的一個很差的地方是,既然知道的虛指針,就能夠根據地址調用相關的虛函數,即便該虛函數是私有的(linux平臺是這樣的)。

4、static

靜態數據成員: 
1) 一個類中能夠有一個或多個靜態成員變量,全部的對象都共享這些靜態成員變量,均可以引用它。

2) static 成員變量和普通 static 變量同樣,編譯時在靜態數據區分配內存,到程序結束時才釋放。這就意味着,static 成員變量不隨對象的建立而分配內存,也不隨對象的銷燬而釋放內存。而普通成員變量在對象建立時分配內存,在對象銷燬時釋放內存。

3) 靜態成員變量必須初始化,並且只能在類體外進行,不用加訪問權限控制符private,public等。例如:

int Student::num = 10;

初始化時能夠賦初值,也能夠不賦值。若是不賦值,那麼會被默認初始化,通常是 0。靜態數據區的變量都有默認的初始值,而動態數據區(堆區、棧區)的變量默認是垃圾值。

4) 靜態成員變量既能夠經過對象名訪問,也能夠經過類名訪問,但要遵循 private、protected 和 public 關鍵字的訪問權限限制。當經過對象名訪問時,對於不一樣的對象,訪問的是同一分內存。

靜態成員函數

  靜態成員函數和靜態數據成員同樣,它們都屬於類的靜態成員,它們都不是對象成員。所以,對靜態成員的引用不須要用對象名。

  靜態成員函數與非靜態成員函數的根本區別是:非靜態成員函數有 this 指針,而靜態成員函數沒有 this 指針。由此決定了靜態成員函數不能訪問本類中的非靜態成員,只能訪問靜態數據成員。

5、const

const限定符的做用就是限定一個變量的值不能改變(由於有的時候咱們但願一個變量的值不被改變)。爲了這個蛋疼的功能,給C++添加了不少複雜性。
一、初始化:因爲const變量的值定了之後不能再改變,因此規定const必須(顯式)初始化。const變量的初始化和變量的初始化同樣,由於從語義上來講,只是變量賦初值而已。
因爲編譯期間,編譯器可能會把程序中const變量替換成其值,因此每一個文件的const變量都應該初始化即定義。爲了支持這一用過,const變量是默認只在本文件內有效。能夠經過extern實現一處實現,多處聲明,這樣編譯器就不會進行值替換。

const數據成員的初始化只能在類的構造函數的初始化列表中進行。但static const類型的初始化仍是在類外初始化。

二、const引用:表示該引用不會改變其綁定的變量的值。const引用和引用的初始化略有不一樣:引用初始化時要求引用類型和變量類型嚴格一致,
但const引用綁定的對象能夠是非const變量(從語義上來講所綁定的對象是否是const對const引用可有可無)或變量的類型能夠轉換成引用的類型。
由於在引用初始化的類型轉換中,會先產生一個臨時量,而後引用綁定的是這個臨時量。這個臨時量是const的,因此非const引用不能綁定該臨時量,可是const引用能夠。編譯器爲了增長引用的使用範圍作了一些工做,使const引用看起來使用範圍更廣。


三、const和指針:常量指針表示指針自己是const,指向常量的指針表示指向的變量是個常量。指針賦值,包括const指針初始化,仍是主要看所指向的變量類型是否一致(能夠進行類型轉化)。
最後,非const引用和非指向const類型的指針不能綁定和指向const變量,const引用和指向const類型的指針能夠綁定和指向非const變量。(緣由很好理解)

6、引用和指針

一、引用即便別名,因此引用定義時必須有一個顯式的對象。引用的類型要與所綁定的類型嚴格匹配,一些const引用的狀況除外。
引用的底層實現仍是指針,經過編譯器編譯後的彙編代碼能夠看出。即編譯過程當中,遇到引用都是變成對引用所綁定對象的地址的操做,因此沒有引用這個變量即引用不是變量。
指針是變量,存放的地址。經過->(成員選擇運算符)能夠操做指定地址的內存(或 解引用和.運算符)。
二、指針和引用的區別:一、指針能夠從新賦值或者進行別的運算,而引用不能夠(有點像常量指針* const p)。二、引用必須初始化,而指針能夠默認初始化(這也有可能出現未定義的狀況)。三、引用的做用就是更加符合人們的使用習慣,並且必定程度上確保操做的安全(不會出現指針操做失誤引發的野指針等狀況,與指針相比)。四、「sizeof 引用」獲得的是所指向的變量(對象)的大小,而「sizeof 指針」獲得的是指針自己(所指向的變量或對象的地址)的大小。

7、智能指針

一、 智能指針是利用RAII(在對象的構造函數中執行資源的獲取(指針的初始化),在析構函數中釋放(delete 指針):這種技法把它稱之爲RAII(Resource Acquisition Is Initialization:資源獲取即初始化))來管理資源。利用的是棧對象的生存週期。

二、解決的問題是new了一個對象,可是忘記delete或者在未delete以前,程序出現異常,致使對象沒有釋放。

(1)auto_ptr,頭文件<memory>C++11提供了更多的智能指針,可是在此以前只有auto_ptr。可是auto_ptr存在的最大的問題是不能共享全部權,能夠轉移全部權,即:

auto_ptr<string> p1(new string("test"));

auto_ptr<string> p2;

p2 = p1;

p1的指針爲NULL,p2的指針指向new string。若是再使用p1,就會形成程序崩潰。在容器中常常有賦值操做,因此,auto_ptr不能用於容器。可是進行簡單的釋放對象操做仍是能夠的,可是要當心使用。

不能使用C++11,可使用boost庫來解決這些問題。

(2)scoped_ptr,頭文件<boost::scoped_ptr.hpp>,與auto_ptr相似,可是不能進行全部權的轉移。上述代碼中,若是使用的是boost::scoped_ptr,則p2 = p1;這條語句,編譯器不會經過。由於boost::scoped_ptr的賦值運算符是私有的。

若是能夠的話儘可能用scoped_ptr來代替auto_ptr。

(3)shared_ptr,頭文件<boost::shared_ptr.hpp>,它維護了一個引用計數器,可以共享全部權,當引用計數器爲0時,才釋放對象。這樣shared_ptr能夠用於STL容器。

因此,要用於容器,或者多個智能指針要指向同一個對象,選擇shared_ptr。

但維護引用計數器,確定要有一些消耗,簡單的替代delete做用,可用scoped_ptr或者auto_ptr。

 9、extern 「C」

  在C++環境下使用C函數的時候,經常會出現編譯器沒法找到obj模塊中的C函數定義,從而致使連接失敗的狀況,應該如何解決這種狀況呢?

  答案與分析:  C++語言在編譯的時候爲了解決函數的重載問題,在生成彙編代碼時會將函數名與參數(或者返回值)聯合起來生成一箇中間的函數名稱,而C語言則不會,所以會形成連接時找不到對應函數的狀況。此時對引用的C函數須要用extern 「C」修飾,這告訴編譯器,請保持個人名稱,用C的方式處理函數名。

相關文章
相關標籤/搜索