C++筆記(一)

C++基礎
1. C++能作什麼
後臺服務程序,可運行於windows/linux系統
    設備接入服務程序(VAG),流媒體服務器(VTDC)、聯網網關(NCG)等。
桌面客戶端程序
    電視牆客戶端、視頻巡邏客戶端等
網頁插件、通常爲IE瀏覽器插件
    預覽控件、回放控件、地圖插件等。
各類SDK程序
    海康的設備網絡SDK(HCNetSDK)、取流庫、播放庫等。
2. 面向對象-基本概念
    對象是對客觀事物的抽象,類是對對象的抽象
    對象(Object)是類(class)的一個實例(Instance)
3. 封裝-基本概念
封裝是指一種將抽象性函式接口的實現細節部分包裝、隱藏起來的方法。
C語言的結構體不具備封裝屬性。
4. 繼承/派生-基本概念
    類的繼承,是新的類從已有類那裏獲得已有的特性,或從已有類產生新
    類的過程就是類的派生。原有的類稱爲基類或父類,產生的新類稱爲派生類或子類。
5. 繼承/派生-注意問題
    1. 用一個類作基類,至關於聲明一個該類的對象,因此必定要有類的定義,只有聲明是不行的
    class emploee;  //只有聲明 沒有定義
    class manager:public emploee{}; //不行, 沒有emploee定義
    2. 派生類可使用基類中全部public和protected的成員
    3. 類對象的構造是自下向上的,首先是基類,然後是成員,再後纔是派生類自己,類對象的銷燬恰好相反。
    4. *構造函數和析構函數沒法繼承,所以在派生類中須要本身對基類的構造函數進行調用。
    5. 構造函數原則:
        1)子類沒有定義構造方法,系統默認調用父類的無參數構造方法。
        2)子類定義了構造方法(不論無參數仍是帶參數),若未顯示調用父類的構造方法,
        首先執行父類無參數的構造方法,而後執行本身的構造方法。
        3)子類定義了構造方法(不論無參數仍是帶參數),若未顯示調用父類的構造方法,
        可是父類只定義了有參構造函數,則會出錯(若是父類只有有參數的構造方法,則子類
        必須顯示調用此帶參構造方法。
        4)子類在構造函數中顯示帶哦用父類帶參數的構造方法,須要初始化父類成員對象的方法。
6. 重載/多態
    多態指發出一樣的消息被不一樣類型的對象接收時有可能致使徹底不一樣的行爲。
    多態性是一種實現「一種接口,多種方法「的技術
    重載也是一種多態,不過是一種特殊形態的多態,是編譯時決定的靜態多態。(編譯時多態)
    多態的三大特徵:重寫、繼承、父類指針指向子類對象(運行時多態)
    
    函數重載:在C++程序中 ,將語義、功能類似的幾個函數用同一個名字表示。
    重載發生在同一個做用域中。
    運算符重載:在C++語言中,用關鍵字operator加上運算符來表示函數。
    虛函數:是C++實現統一接口的途徑,在基類中定義具備通用接口(泛化)的虛函數,在派生類中將虛函數實現(特化)。
7. 爲何使用函數重載?
    1. 便於記憶,提升函數易用性。
    2. 類的構造函數須要重載機制。
8. 重載如何區分,靠參數,編譯器根據參數爲每一個重載函數分配不一樣的標識符。
9. 運算符重載的規則
    1)只能重載C++中已有的運算符
    2)類屬關係運算符「.」、做用域分辨符「::」、成員指針運算符「*」、sizeof運算符
    和三目運算符「?:」不能重載
    3)重載以後的運算符的優先級和結合性都不能變,單目運算符只能重載爲單目運算符,雙目運算符
    只能重載爲雙目運算符。
    4)運算符重載後的功能應該與原有功能相相似。
    5)重載運算符含義必須清楚,不能有二義性。
10. 虛函數與重載的關係
    1)虛函數的重載要求函數名、返回類型、參數個數、參數類型以及參數的順序都與基類中原型徹底相同,
    不能有任何的不一樣。
    2)通常函數的重載,只要函數名相同便可,函數的返回類型以及所帶的參數能夠不一樣。
11. 虛函數規則
    1)只有成員函數才能聲明爲虛函數,由於虛函數僅適用於有繼承關係的類對象,因此普通函數不能聲明爲虛函數。
    2)虛函數必須是非靜態成員函數,由於靜態成員函數不受限於某個對象。
    3)內聯函數不能聲明爲虛函數,由於內聯函數不能在運行中動態的肯定其位置。
    4)構造函數不能聲明爲虛函數,多態是指不一樣的對象對同一消息有不一樣的行爲特性。
    虛函數做爲運行過程當中多態的基礎,主要是針對對象的,而構造函數是在對象產生以前
    運行的,所以,虛構造函數是沒有意義的。
    5)析構函數能夠聲明爲虛函數,析構函數的功能是在該類對象消亡以前進行一些必要的清理工做,析構函數沒有類型,
    也沒有參數,和普通成員函數相比,虛析構函數狀況略微簡單些。
12. 純虛函數
    概念:一個在基類中沒有定義具體操做內容的虛函數,要求各派生類根據實際須要定義本身的實現內容。
    聲明形式爲:virtual <函數類型> <函數名> (參數表) = 0
    抽象類:
        主要做用是經過它爲一個類族創建一個公共接口,使他們可以更有效的發揮多態特性。
        一個抽象類至少帶有一個純虛函數。
13. 純虛函數規則
    1)抽象類只能用做其餘類的基類,不能創建抽象類對象
    抽象類處於繼承層次結構的較上層,一個抽象類自身沒法實例化,只能經過繼承機制,生成抽象類的非抽象派生類,而後再實例化。
    2)抽象類不能用做參數類型、函數返回值或顯示轉換的類型。
    3)能夠聲明一個抽象類的指針和引用。
14. 單例對象
    1. 確保一個類只有一個實例被創建
    2. 提供了一個對象的全局訪問點,被全部程序模塊共享
    爲了防止在外部調用累的構造函數而構造實例,須要將構造函數的訪問權限標記爲protect或private,最後,
    須要提供要給全局訪問點,就須要在類中定義一個static函數,返回在類內部惟一構造的實例。
15. 靜態對象
    局部靜態對象:局部靜態變量在程序第一次執行定義語句時初始化,直到程序終止被銷燬,其生命週期貫穿函數
    調用及以後的時間。
    int count_calls(){
        static int ctr = 0; //調用結束後仍然存在
        renturn ++ctr;
    }
    類的靜態成員:在類的成員變量前加上static,可使該成員與類關聯在一塊兒,從而不與任何對象綁定,
    被該類及其派生類的全部對象共享。
    類的靜態成員不在建立類的對象時被定義,所以也不是由類的構造函數初始化。通常來講在類的外部定義和初始化類的靜態成員。
    class bass{
    public:
        static int _num; //類內聲明
    }
    int base::_num = 0;  //類外定義
    
    class derived:public base{
    };
    
    main()
    {
    base a;
    derived b;
    a._num++;
    cout << "base class static data number _num is" << a._num << endl;
    b._num++;
    cout << "derived class static data number _num is" << b._num << endl;
    }//結果爲1, 2; 可見派生類與基類共用一個靜態數據成員
    
    靜態成員變量能夠是類自己的對象,普通成員變量不能夠;
    class base{
    public:
    static base _object1; //正確,靜態數據成員
    base _object2; //錯誤
    base *pObject; // 正確,指針
    base &mObject; // 正確,引用
    }
    
    靜態成員變量能夠做爲成員函數的默認參數,普通變量不能夠
    class base{
    public:
    static int _staticVar;
    int _var;
    void foo1(int i = _staticVar);//正確, _staticVar爲靜態數據成員
    void foo2(int i =  _var);//錯誤,_var爲普通數據成員
    }
16.靜態成員函數
    1)靜態成員函數不與類的對象相關聯,即函數內不含this指針,所以只能訪問類的靜態成員變量與靜態成員函數,不能調用類內非靜態成員。
    2)與非靜態成員函數相比,靜態成員函數不是須要等待類的對象初始化,在沒有對象被初始化時也能夠直接經過類進行調用。
    3)靜態成員函數的地址可用普通函數指針存儲,而普通成員函數地址須要用類成員函數指針來存儲。
    class base{
    static int fun1();
    int fun2();
    };
    int (*pf1)() = &base::func1;//普通函數指針
    int (base::*pf2)() = &base::func2;//成員函數指針
    
17. 指針的基本概念
    1)引用是某一變量的一個別名,對引用的操做與對變量直接操做徹底同樣。
    2)指針是指向另外一種類型的複合類型,保存制定對象的地址。經過指針對象能夠實現對其餘對象的間接訪問,這一點與引用相似。
    3)指針依賴於指向的類型,其不只所指向對象在內存空間的位置,還包含所指向對象佔用的內存空間大小。
    4)相對於引用,指針自己是一個對象,聲明時能夠不賦初值,也能夠在生命週期內更改所指向的對象。
18. 指針常量與常量指針
    指針常量:指針裏面所存儲的內容(內存地址)是常量,不能改變 
    聲明格式爲:數據類型 *const 指針變量
    常量指針:指針指向的是常量,他指向的內容不能發生改變  
    聲明格式爲:數據類型 const *指針變量
19. 數組指針與指針數組
    數組指針:即指向數組的指針,如 int(*a)[4]
    指針數組:用於存儲指針的數組,也就是數組元素都是指針,如 int* a[4]
數組指針:
    通常來講編譯器會直接將數組轉換爲指針,便可以將數組名直接看成數組第一個成員的指針來使用。
指針數組;
    因爲指針也是對象,所以能夠聲明指針的數組,用來保存一列相同對象的集合。
    定義指向int類型的指針的數組,其中[]優先級更高,即p爲一個n維數組,其次爲*,即爲指針數組,最後爲int,即爲指向int類型的指針的數組。
    結合數組指針的內容,能夠將二位數組的每一行看成指針,則二維數組與指針的數組相似。
    int *p[3];
    int a[3][4];
    for (int i = 0; i < 3; i++)
        p[i] = a[i];
20. 指針參數
    1)指針能夠做爲函數的參數,傳遞給函數體內的代碼使用。當指針做爲函數形參時,其行爲
    與其餘非引用類型同樣,實參的值會被拷貝給形參。因爲指針能夠間接訪問指向的對象,
    所以經過被拷貝的形參能夠指向實參所指向的對象,函數體中對所指對象的修改能夠被保留。
    2)當所指的對象佔用內存控件較大時,經過指針形參能夠避免對象的拷貝操做,轉爲指針的拷貝操做,提升效率。
21. 函數指針
    函數指針指向函數而非對象,其指向的類型由函數的返回類型和形參類型共同決定。
    bool (*pf)(int, int*); //定義了一個函數指針,指向返回類型爲bool,形參爲int和int*的函數。
    函數名會被自動看成函數指針來處理
    bool chk(int, int*){return false;}
    pf = chk;
    函數指針做爲指針,能夠成爲其餘函數的形參,也能夠做爲其餘函數的返回值。
    int compare(int, int, int(*pf)(int, int)); //定義了函數compare(int, int, int(*pf)(int, int))
    int (*f1(int))(int*, int);//定義了函數f1, 形參爲int, 返回類型爲int(*)(int*, int)
22. this指針
    在每一個成員函數中都包含了一個特殊的指針,稱爲this指針,他是指向本類對象的指針,他的值是當前被
    調用成員函數所在對象的起始地址。
23. 內存分區
    棧(stack)
    局部變量存儲區,函數內部、實參的局部變量,由編譯器負責分配,函數結束,棧變量失效。
    堆(heap)
    用戶自行分配,使用new/delete、deelte[]進行釋放(malloc\calloc\relloc\  free), 成對出現,申請則釋放
    不然形成內存泄露。
    全局區/靜態區(global static area)
    全局變量和靜態變量存放區,程序一經編譯好,該區域便存在。全局變量和靜態變量編譯器會給這些變量自動初始化賦值。
    因爲全局變量一直佔據內存空間且不易維護,推薦少用。程序結束時釋放。
    常量區
    這是一塊比較特殊的存儲區,專門存儲不能修改的常量,如字符串常量。
    代碼區
    函數體的二進制代碼。
24. 堆棧區別
                棧                            堆
    存儲對象     局部變量、函數參數等        malloc或new出來的對象
    大小        小,vc缺省大小爲2M            大,受限於機器的虛擬內存大小
    速度        快                            相對棧來講較慢
    結構        先進後出                    無嚴格的數據結構
    內存連續    內存連續,不會出現碎片問題    頻繁的new/delete等操做勢必會形成內存空間的不連續,從而形成大量的碎片,使程序的效率下降
    管理        操做系統維護                使用者維護
25. 內容操做注意事項
    1)用malloc或new申請內存以後,應該當即檢查指針值是否爲NULL,防止使用指針值爲NULL的內存
    2)不要忘記爲數組或動態內存賦初值(好比calloc比malloc就要好),指針初始化爲NULL(c++爲0)
    3)避免數組或指針下標越界,特別小心發生 多1 或者 少1 的操做
    4)動態內存的申請和釋放必須配對,防止內存泄露,具體爲malloc/calloc/realloc和free配對,new和delete以及delete[]配對
    5)用free或delete釋放內存後,應當即將指針設置爲NULL(C++中爲0),防止 野指針 懸垂指針
    6)類的內存大小  c++中空類/結構體的內存大小爲1字節
26. 內存池 一種內存分配方式,提升內存的使用效率
    在真正使用內存以前,先申請分配必定數量的、大小相等(通常狀況下)的內存塊留做備用,當有新的內存需求時,就從內存池中分出一部份內存塊,若內存塊不夠再申請新的內存。
    解決頻繁調用new/delet產生的性能消耗
    減小內存碎片
27.    內存池分類
    線程安全角度
        單線程內存池:整個生命週期只被一個線程使用,所以不須要考慮互斥訪問的問題。性能更高
        多線程內存池:可能被多個線程共享,所以則須要在每次分配和釋放內存時加鎖。適用範圍更廣
    可分配單元大小
        固定內存池:應用程序每次從內存池中分配出來的內存單元大小事先已經肯定,是固定不變的。
        可變內存池:每次分配的內存單元大小按需變化。
28. STL容器
    序列容器:線性結構的可序羣集,容器內的元素按順序進行存放,可按位置存儲和訪問,元素排列次序與元素無關,
    而是由元素添加到容器內的次序決定的。
    vector、list、deque、string
    
    關聯容器:非線性結構,更準確說是二叉樹結構,各元素之間沒有嚴格的物理上的順序關係,即元素
    在容器中並滅有保存元素植入容器時的邏輯順序。以鍵值的方式保存數據,把關鍵字或值關聯起來保存鍵值對容器,元素按鍵進行排序。
    set、hash_set、multiset、hash_map、map、hash_multiset、multimap、hash_multimap
29. 迭代器
    迭代器用來訪問一個容器類的所包含的所有元素,其行爲像一個指針。
    迭代器是個所謂的複雜的指針,具備遍歷複雜數據結構的能力。其下層運行機制取決於遍歷的數據結構。
30. vector 
    vector可採用連續存儲空間來存儲元素。能夠採用下標對vector元素進行訪問
    vector在訪問元素時候更加高效,在末尾添加和刪除元素相對高效。對於其餘不在末尾的刪除和插入操做,效率更低。
    遍歷方法;下標法 和 迭代器
31. list 
    一種序列型容器,是一個線性鏈表結構,他的數據由若干個節點構成,無需分配製定的內存大小且能夠任意伸縮,這是由於
    他存儲在非連續的內存空間中,而且由指針將有序的元素連接起來。
    因爲是鏈表結構,list隨機檢索的性能很是的很差,可是他能夠迅速的在任何節點進行插入和刪除操做。
    遍歷方法: 迭代器
32. map
    一種關聯型容器,一個鍵值對(key/value)容器,map與multimap差異僅僅在於multimap容許一個鍵對應多個值。
    map內部是自建一顆紅黑樹(一種非嚴格意義上的平衡二叉樹),這棵樹具備對數據自動排序的功能,因此在map內部
    全部的數據都是有序的。其「鍵「子啊容器中不能夠重複,且按必定順序排列,能夠修改實值,可是不能修改key
    map的檢索操做比vector慢,比list快。
    遍歷方法:迭代器法 和 賦值
    刪除元素:注意迭代器的位置
33. 文件讀寫
    C操做 ANSI C提供的一組標準庫函數實現 fopen、fseek、fread、fwrite、fclose
    MFC操做 MFC提供的CFile類
    WINAPI win32 api函數 CreateFile
    C++操做 C++提供的文件操做類ofstream、ifstream、fstram
34. 常引用、常對象和對象的常成員
    http://www.jizhuomi.com/software/68.html
    1)常引用
    用const聲明的引用就是常引用。常引用所引用的對象不能被更改。咱們常常見到的是常引用做爲函數的形參,這樣不會發生對實參的誤修改。
    常引用的聲明形式爲:const 類型說明符 &引用名。
    #include<iostream>
    using namespace std;
    void show(const double& r);
    int main()
    {  
           double d(9.5);
           how(d);
           return 0;
    }
    void show(const double& r)
    //常引用做形參,在函數中不能更新r所引用的對象。
    {  
          cout<<r<<endl;  
    }
    2)常對象
    所謂常對象,是指數據成員在它的生存期內不會被改變。定義常對象時必須對其進行初始化,而且不能改變其數據成員的值。
    常對象的聲明形式爲:類名 const 對象名 或者 const 類名 對象名。
    class A
    {
    public:
               A(int i,int j) {x=i; y=j;}
                 ...
    private:
              int x,y;
    };
    A const a(6,8); //a是常對象,不能被更新
   說明: 若是程序中出現對常對象的數據成員修改的語句,編譯器會報錯。
   通常修改對象的數據成員有兩種途徑,一種是經過對象名訪問公有數據成員並修改其值,而常對象的數據成員是不能被修改的;
   另外一種是類的成員函數修改數據成員的值,而常對象不能調用普通的成員函數。但是這樣的話,常對象就只剩數據,沒有對外的接口了,
   這就須要爲常對象專門定義的常成員函數了。
   3)類的常成員函數
   類中用const聲明的成員函數就是常成員函數。
   常成員函數的聲明形式爲:類型說明符 函數名(參數表) const。
   a.常成員函數在聲明和實現時都要帶const關鍵字;
   b.常成員函數不能修改對象的數據成員,也不能訪問類中沒有用const聲明的很是成員函數;
   c.常對象只能調用它的常成員函數,不能調用其餘的普通成員函數;
   d.const關鍵字能夠被用於參與對重載函數的區分,好比,若是有兩個這樣聲明的函數:void fun(); void fun() const;,則它們是重載函數。
    #include<iostream>
    using namespace std;
    class R
    {
    public:
                R(int r1, int r2)    { R1=r1; R2=r2; }
                void print();
                void print() const;
    private:
                int R1,R2;
    };
    void R::print()
    {
               cout<<R1<<":"<<R2<<endl;
    }
    void R::print() const
    { 
              cout<<R1<<";"<<R2<<endl;
    }
    int main()
    {
             R a(5,4);
             a.print(); //調用void print()
             const R b(20,52); 
             b.print(); //調用void print() const
             return 0;
    }
































































































































































































































































































































html

    上面的R類中聲明瞭兩個同名函數print,第二個是常成員函數。
    在main函數中定義了兩個對象a和b,b是常對象,經過a調用的是沒有用const聲明的函數,而經過b調用的是用const聲明的常成員函數。
    4)類的常數據成員
    類的數據成員也能夠是常量和常引用,用const聲明的數據成員就是常數據成員。在任何函數中都不能對常數據成員賦值。構造函數對常數據成員初始化,只能經過初始化列表。
    #include<iostream>
    using namespace std;
    class A
    {
    public:
                   A(int i);
                   void print();
                   const int& r;
    private:
                   const int a;
                   static const int b;  //靜態常數據成員
    };
    const int A::b=20;
    A::A(int i):a(i),r(a) {}
    void A::print()
    {   
                 cout<<a<<":"<<b<<":"<<r<<endl;
    }
    int main()
    {
               //創建對象a和b,並以50和10做爲初值,分別調用構造函數,經過構造函數的初始化列表給對象的常數據成員賦初值
               A a1(50),a2(10); 
               a1.print();
               a2.print();
               return 0;
    }




























linux

    此程序的運行結果是:ios

    50:20:50
    10:20:10
35. C++ const修飾函數、函數參數、函數返回值
    https://www.runoob.com/w3cnote/cpp-const-keyword.html
    https://www.cnblogs.com/wjxx836/p/4440823.html
36. 互斥鎖、遞歸鎖、讀寫鎖和自旋鎖區別
    https://www.cnblogs.com/evenleee/p/11309156.html





c++

    互斥鎖
    共享資源的使用是互斥的,即一個線程得到資源的使用權後就會將改資源加鎖,使用完後會將其解鎖,因此在使用過程當中有其它線程想要獲取該資源的鎖,那麼它就會被阻塞陷入睡眠狀態,直到該資源被解鎖纔會別喚醒,若是被阻塞的資源不止一個,那麼它們都會被喚醒,
    可是得到資源使用權的是第一個被喚醒的線程,其它線程又陷入沉睡。

windows

    遞歸鎖
    同一個線程能夠屢次得到該資源鎖,別的線程必須等待該線程釋放全部次數的鎖才能得到。
api

    讀寫鎖
    讀寫鎖擁有讀狀態加鎖、寫狀態加鎖、不加鎖三種狀態。只有一個線程能夠佔有寫狀態的鎖,
    但能夠多個線程同時佔有讀狀態鎖,這也是它能夠實現高併發的緣由。
    當其處於寫狀態鎖下,任何想要嘗試得到鎖的線程都會被阻塞,直到寫狀態鎖被釋放;
    若是是處於讀狀態鎖下,容許其它線程得到它的讀狀態鎖,可是不容許得到它的寫狀態鎖,
    當讀寫鎖感知到有線程想要得到寫狀態鎖時,便會阻塞其後全部想要得到讀狀態鎖的線程。
    因此讀寫鎖很是適合資源的讀操做遠多於寫操做的狀況。





數組

    讀寫鎖三個特徵:瀏覽器

    多個讀者能夠同時進行讀
    寫者必須互斥,只容許一個寫者寫,也不能讀者寫者同時進行
    寫者優先於讀者,一旦有寫者,則後續讀者必須等待,喚醒時優先考慮寫者
    自旋鎖
    自旋鎖是一種特殊的互斥鎖,當資源被加鎖後,其它線程想要再次加鎖,
    此時該線程不會被阻塞睡眠而是陷入循環等待狀態(不能再作其它事情),
    循環檢查資源持有者是否已經釋放了資源,這樣作的好處是減小了線程
    從睡眠到喚醒的資源消耗,但會一直佔用CPU資源。適用於資源的鎖被持
    有的時間短,而不但願在線程的喚醒上花費太多資源的狀況。







安全

    自旋鎖的目的服務器

    自旋鎖的實現是爲了保護一段短小的臨界區操做代碼,保證這個臨界區的操做是原子的,
    從而避免併發的競爭冒險。在Linux內核中,自旋鎖一般用於包含內核數據結構的操做,
    你能夠看到許多內核數據結構中都嵌入有spinlock,這些大部分就是用於保護它自身被操做的原子性,
    在操做這樣的結構體時都經歷這樣的過程:上鎖-操做-解鎖。


    若是內核控制路徑發現自旋鎖「開着」(能夠獲取),就獲取並繼續本身的執行。     相反,若是內核控制路徑發現鎖由運行在另外一個CPU上的內核控制路徑「鎖着」,     就在原地「旋轉」,反覆執行一條緊湊的循環檢測指令,直到鎖被被釋放。自旋鎖是循環檢測「忙等」,     即等待時內核無事可作,進程在CPU上保持運行,因此它的臨界區必須小,且操做過程必須短。不過,     自旋鎖一般很是方便,由於不少內核資源只鎖1毫秒的時間片斷,因此等待自旋鎖的釋放不會消耗太多CPU的時間。

相關文章
相關標籤/搜索