有那麼一些零碎的小知識點,偶爾很迷惑,偶爾被忽略,偶然卻發現它們很重要,這段時間正好在溫習這些,就整理在這裏,一塊兒學習一塊兒提升!後面還會繼續補充。html
——前言程序員
1.面向對象的特性web
封裝、繼承、多態。正則表達式
封裝:把客觀事物封裝成抽象的類,而且類能夠把本身的數據和方法只讓可信的類或者對象操做,對不可信的進行信息隱藏。(優勢:能夠隱藏實現細節,使得代碼模塊化)算法
繼承:可使用現有類的全部功能,並在無需從新編寫原來的類的狀況下對這些功能進行擴展。(優勢:能夠擴展已存在的代碼模塊(類))編程
多態:一個類實例的相同方法在不一樣情形有不一樣表現形式。多態機制使具備不一樣內部結構的對象能夠共享相同的外部接口。雖然針對不一樣對象的具體操做不一樣,但經過一個公共的類,這些操做能夠經過相同的方式被調用。安全
多態實現的兩種方式:父類指針指向子類對象 或 將一個基類的引用類型賦值爲它的派生類實例。(重要:虛函數 + 指針或引用)數據結構
構造函數、複製構造函數、析構函數、賦值運算符不能被繼承。模塊化
2.堆和棧函數
從內存角度來講:棧區(stack)由編譯器自動分配釋放,存放函數的參數值,局部變變量的值等,其操做方式相似於數據結構中的棧,可靜態亦可動態分配。
堆區(heap)通常由程序員分配釋放,若程序員不釋放,可能形成內存泄漏,程序結束時可能由OS回收。只可動態分配,分配方式相似於鏈表。
從數據結構角度來講:堆能夠被當作是一棵樹,如:堆排序。
而棧是一種先進後出的數據結構。
3.malloc和new
1.malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。但它們均可用於申請動態內存和釋放內存。
2.對於非內部數據類型的對象而言,用maloc/free沒法知足動態對象的要求。對象在建立的同時要自動執行構造函數,對象在消亡以前要自動執行析構函數。由malloc/free是庫函數而不是運算符,不在編譯器控制權限以內,不可以把執行構造函數和析構函數的任務強加於malloc/free,所以C++語言須要一個能完成動態內存分配和初始化工做的運算符new,和一個能完成清理與釋放內存工做的運算符delete。
3.new能夠認爲是malloc加構造函數的執行。new出來的指針是直接帶類型信息的。而malloc返回的都是void*指針。new delete在實現上其實調用了malloc,free函數。
4.new 創建的是一個對象;malloc分配的是一塊內存。
4.虛函數實現機制,虛繼承在sizeof中有沒有影響,構造函數可否爲虛函數,與純虛函數
虛函數表:類的虛函數表是一塊連續的內存,每一個內存單元中記錄一個JMP指令的地址。
編譯器會爲每一個有虛函數的類建立一個虛函數表,該虛函數表將被該類的全部對象共享。類的每一個虛函數佔據虛函數表中的一塊。若是類中有N個虛函數,那麼其虛函數表將有N*4字節的大小。
在有虛函數的類的實例中分配了指向這個表的指針的內存,因此,當用父類的指針來操做一個子類的時候,這張虛函數表就顯得尤其重要了,它就像一個地圖同樣,指明瞭實際所應該調用的函數。
編譯器應該是保證虛函數表的指針存在於對象實例中最前面的位置(這是爲了保證取到虛函數表的有最高的性能——若是有多層繼承或是多重繼承的狀況下)。 這意味着能夠經過對象實例的地址獲得這張虛函數表,而後就能夠遍歷其中函數指針,並調用相應的函數。
->有虛函數或虛繼承的類實例化後的對象大小至少爲4字節(確切的說是一個指針的字節數;說至少是由於還要加上其餘非靜態數據成員,還要考慮對齊問題);沒有虛函數和虛繼承的類實例化後的對象大小至少爲1字節(沒有非靜態數據成員的狀況下也要有1個字節來記錄它的地址)。
有純虛函數的類爲抽象類,不能定義抽象類的對象,它的子類要麼實現它全部的純虛函數變爲一個普通類,要麼仍是一個抽象類。
特別的:
(1)當存在類繼承而且析構函數中有必需要進行的操做時(如須要釋放某些資源,或執行特定的函數)析構函數須要是虛函數,不然若使用父類指針指向子類對象,在delete時只會調用父類的析構函數,而不能調用子類的析構函數,從而形成內存泄露或達不到預期結果;
(2)內聯函數不能爲虛函數:內聯函數須要在編譯階段展開,而虛函數是運行時動態綁定的,編譯時沒法展開;
(3)構造函數不能爲虛函數:構造函數在進行調用時還不存在父類和子類的概念,父類只會調用父類的構造函數,子類調用子類的,所以不存在動態綁定的概念;可是構造函數中能夠調用虛函數,不過並無動態效果,只會調用本類中的對應函數;
(4)靜態成員函數不能爲虛函數:靜態成員函數是以類爲單位的函數,與具體對象無關,虛函數是與對象動態綁定的。
更多關於虛函數的細節,請移步博文:關於C++虛函數表的那些事兒
5.面向對象的多態、多態的實現機制,多態的例子
見知識點4
6.對一個類求sizeof須要考慮的內容
見知識點4。同時,對於一個結構體和一個類執行sizeof()運算時狀況比較複雜,詳細分析請移步另外一篇博文struct/class等內存字節對齊問題詳解
7.重載和重寫(覆蓋)
方法的重寫Overriding和重載Overloading是多態性的不一樣表現。
重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中多態性的一種表現。
若是在子類中定義某方法與其父類有相同的名稱和參數,咱們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被「屏蔽」了,並且若是子類的方法名和參數類型和個數都和父類相同,那麼子類的返回值類型必須和父類的相同;若是在一個類中定義了多個同名的方法,它們或有不一樣的參數個數或有不一樣的參數類型,則稱爲方法的重載(Overloading)。Overloading的方法是能夠改變返回值的類型。也就是說,重載的返回值類型能夠相同也能夠不一樣。
8.「引用」與多態的關係?
引用是除指針外另外一個實現多態的方式。這意味着,一個基類的引用能夠指向它的派生類實例。例:
Class A; Class B : Class A{…};
B b; A& ref = b;
9.計算機加載程序包括哪幾個區?
一個由C/C++編譯的程序佔用的內存分爲如下幾個部分:
(1)棧區(stack):—由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。可靜態也可動態分配。其操做方式相似於數據結構中的棧。
(2)堆區(heap):通常由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。動態分配。注意它與數據結構中的堆是兩回事,分配方式卻是相似於鏈表。
(3)全局區(靜態區):—程序結束後由系統釋放,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域;未初始化的全局變量和靜態變量在相鄰的另外一塊區域(BSS,Block Started by Symbol),在程序執行以前BSS段會自動清0。
(4)文字常量區:—程序結束後由系統釋放,常量字符串就是放在這裏的。
(5)程序代碼區:—存放函數體的二進制代碼。
10.派生類中構造函數與析構函數,調用順序
構造函數的調用順序老是以下:
1.基類構造函數。若是有多個基類,則構造函數的調用順序是某類在類派生表中出現的順序,而不是它們在成員初始化表中的順序。
2.成員類對象構造函數。若是有多個成員類對象則構造函數的調用順序是對象在類中被聲明的順序,而不是它們出如今成員初始化表中的順序。若是有的成員不是類對象,而是基本類型,則初始化順序按照聲明的順序來肯定,而不是在初始化列表中的順序。
3.派生類構造函數。
析構函數正好和構造函數相反。
11.extern 「C」的做用
extern "C"實現C++與C及其它語言的混合編程,是用在C和C++之間的橋樑。之因此須要這個橋樑是由於C編譯器編譯函數時不帶函數的類型信息,只包含函數符號名字;而C++編譯器爲了實現函數重載,編譯時會帶上函數的類型信息,如他把上面的a函數可能編譯成_a_float這樣的符號爲了實現重載。
extern "C"的慣用法:
在C++中引用C語言中的函數和變量,在包含C語言頭文件(假設爲cExample.h)時,需進行下列處理:
extern "C"{
#include "cExample.h"
}
而在C語言的頭文件中,對其外部函數只能指定爲extern類型,C語言中不支持extern "C"聲明,在.c文件中包含了extern "C"時會出現編譯語法錯誤。
extern自己做爲關鍵字修飾變量(函數)時聲明該變量(函數)是外部變量(函數),一般全局變量在頭文件中用這種方式聲明,在對應源文件中定義,來防止重定義的錯誤。
12.析構函數、構造函數能不能被繼承
見知識點1
13.C++爲何用模板類,爲何用泛型
經過泛型能夠定義類型安全的數據結構(類型安全),而無須使用實際的數據類型(可擴展)。這可以顯著提升性能並獲得更高質量的代碼(高性能),由於您能夠重用數據處理算法,而無須複製類型特定的代碼(可重用)。
14.結構體內存對齊,與什麼有關(CPU)
在系統默認的對齊方式下:每一個成員相對於這個結構體變量地址的偏移量正好是該成員類型所佔字節的整數倍,且最終佔用字節數爲成員類型中最大佔用字節數的整數倍。
詳細分析見博客:struct/class等內存字節對齊問題詳解
爲何要對齊?當CPU訪問正確對齊的數據時,它的運行效率最高,當數據大小的數據模數的內存地址是0時,數據是對齊的。例如:WORD值應該是老是從被2除盡的地址開始,而DWORD值應該老是從被4除盡的地址開始,數據對齊不是內存結構的一部分,而是CPU結構的一部分。當CPU試圖讀取的數值沒有正確的對齊時,CPU能夠執行兩種操做之一:產生一個異常條件;執行屢次對齊的內存訪問,以便讀取完整的未對齊數據,若屢次執行內存訪問,應用程序的運行速度就會慢。
15.指針和引用
1.指針是一個變量,只不過這個變量存儲的是一個地址,指向內存的一個存儲單元;而引用跟原來的變量實質上是同一個東西,只不過是原變量的一個別名而已。
2.指針能夠有多級,可是引用只能是一級;
3.指針的值能夠爲空,也可能指向一個不肯定的內存空間,可是引用的值不能爲空,而且引用在定義的時候必須初始化爲特定對象;(所以引用更安全)
4.指針的值在初始化後能夠改變,即指向其它的存儲單元,而引用在進行初始化後就不會再改變引用對象了;
5.sizeof引用獲得的是所指向的變量(對象)的大小,而sizeof指針獲得的是指針自己的大小;
6.指針和引用的自增(++)運算意義不同;
16.static關鍵字做用
在C語言中,關鍵字static有三個明顯的做用:
1)在函數體內,一個被聲明爲靜態的變量在這一函數被調用過程當中維持上一次的值不變,即只初始化一次(該變量存放在靜態變量區,而不是棧區)。
2)在模塊內(但在函數體外),一個被聲明爲靜態的變量能夠被模塊內所用函數訪問,但不能被模塊外訪問。(注:模塊能夠理解爲文件)
3)在模塊內,一個被聲明爲靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地範圍內使用。
【補充】《C和指針》中說static有兩層含義:指明存儲屬性;改變連接屬性。
具體解釋:(1)全局變量(包括函數)加上static關鍵字後,連接屬性變爲internal,也就是將他們限定在了本做用域內;(2)局部變量加上static關鍵字後,存儲屬性變爲靜態存儲,不存儲在棧區,下一次將保持上一次的尾值。
除此以外,C++中還有新用法:
4)在類中的static成員變量意味着它爲該類的全部實例所共享,也就是說當某個類的實例修改了該靜態成員變量,其修改值爲該類的其它全部實例所見;
5)在類中的static成員函數屬於整個類所擁有,這個函數不接收this指針,於是只能訪問類的static成員變量(固然,能夠經過傳遞一個對象來訪問其成員)。
17.虛表,基類的虛表是什麼樣的,派生類虛表
(1)單繼承狀況
(2)多重繼承(無虛函數覆蓋)
(3)多重繼承(有虛函數覆蓋)
詳細的內容參考博文:關於C++虛函數表的那些事兒
18.volatile
volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示能夠被某些編譯器未知的因素更改,好比:操做系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就再也不進行優化,從而能夠提供對特殊地址的穩定訪問。
當要求使用volatile 聲明的變量的值的時候,系統老是從新從它所在的內存讀取數據,即便它前面的指令剛剛從該處讀取過數據。並且讀取的數據馬上被保存。
volatile 指出 i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,於是編譯器生成的彙編代碼會從新從i的地址讀取數據放在b中。而優化作法是,因爲編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操做,它會自動把上次讀的數據放在b中。而不是從新從i裏面讀。這樣一來,若是i是一個寄存器變量或者表示一個端口數據就容易出錯,因此說volatile能夠保證對特殊地址的穩定訪問。
19.#define與const的區別
• define不會作類型檢查(容易出錯),const擁有類型,會執行相應的類型檢查
• define僅僅是宏替換,不佔用內存,而const會佔用內存
• const內存效率更高,編譯器可能將const變量保存在符號表中,而不會分配存儲空間,這使得它成 爲一個編譯期間的常量,沒有存儲和讀取的操做
當使用#define定義一個簡單的函數時,強烈建議使用內聯函數替換!
20.C++中的強制類型轉換
• reinterpret_cast: 轉換一個指針爲其它類型的指針。它也容許從一個指針轉換爲整數類型,反之亦 然. 這個操做符可以在非相關的類型之間轉換. 操做結果只是簡單的從一個指針到別的指針的值的 二進制拷貝. 在類型之間指向的內容不作任何類型的檢查和轉換?
class A{}; class B{}; A* a = new A; B* b = reinterpret_cast(a);
• static_cast: 容許執行任意的隱式轉換和相反轉換動做(即便它是不容許隱式的),例如:應用到類 的指針上, 意思是說它容許子類類型的指針轉換爲父類類型的指針(這是一個有效的隱式轉換), 同 時, 也可以執行相反動做: 轉換父類爲它的子類
class Base {}; class Derive:public Base{}; Base* a = new Base; Derive *b = static_cast(a);
• dynamic_cast: 只用於對象的指針和引用. 當用於多態類型時,它容許任意的隱式類型轉換以及相 反過程. 不過,與static_cast不一樣,在後一種狀況裏(注:即隱式轉換的相反過程),dynamic_cast 會檢查操做是否有效. 也就是說, 它會檢查轉換是否會返回一個被請求的有效的完整對象。檢測在 運行時進行. 若是被轉換的指針不是一個被請求的有效完整的對象指針,返回值爲NULL. 對於引用 類型,會拋出bad_cast異常。
• const_cast: 這個轉換類型操縱傳遞對象的const屬性,或者是設置或者是移除,例如:
class C{}; const C* a = new C; C *b = const_cast(a);
21.析構函數中拋出異常時歸納性總結
(1) C++中析構函數的執行不該該拋出異常;
(2) 假如析構函數中拋出了異常,那麼系統將變得很是危險,也許很長時間什麼錯誤也不會發生;但也許系統有時就會莫名奇妙地崩潰而退出了,並且什麼跡象也沒有;
(3) 當在某一個析構函數中會有一些可能(哪怕是一點點可能)發生異常時,那麼就必需要把這種可能發生的異常徹底封裝在析構函數內部,決不能讓它拋出函數以外,即在析構函數內部寫出完整的throw...catch()塊。
22.C++11新特性
Lambda、變參模板、auto、decltype、constexpr、智能指針、列表初始化、正則表達式、線程庫、靜態斷言、委託構造。
weak_ptr被設計爲與shared_ptr共同工做,能夠從一個shared_ptr或者另外一個weak_ptr對象構造,得到資源的觀測權。但weak_ptr沒有共享資源,它的構造不會引發指針引用計數的增長。
使用weak_ptr的成員函數use_count()能夠觀測資源的引用計數,另外一個成員函數expired()的功能等價於use_count()==0,但更快,表示被觀測的資源(也就是shared_ptr的管理的資源)已經不復存在。
weak_ptr可使用一個很是重要的成員函數lock()從被觀測的shared_ptr得到一個可用的shared_ptr對象, 從而操做資源。但當expired()==true的時候,lock()函數將返回一個存儲空指針的shared_ptr.
————————————————————————我是分割線———————————————————————
C++系列的暫時整理到這裏吧,若是讀者發現還有哪些這方面的經典常考知識點也請指出,待續~
轉載請註明出處:http://www.cnblogs.com/webary/p/4754522.html,謝謝!