小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

重磅干貨:五萬字長文總結(jié):C/C 知識(上篇)

 goandlove 2019-08-18
結(jié)識更多同行,共同討論“嵌入式”技術(shù)。歡迎添加社區(qū)客服微信,備注發(fā)送“電源+公司名(學(xué)校)+職位(專業(yè))”拉您入群。

目錄

  • C/C++

  • STL

  • 數(shù)據(jù)結(jié)構(gòu)

  • 算法

  • Problems

  • 操作系統(tǒng)

  • 計算機網(wǎng)絡(luò)

  • 網(wǎng)絡(luò)編程

  • 數(shù)據(jù)庫

  • 設(shè)計模式

  • 鏈接裝載庫

  • 海量數(shù)據(jù)處理

  • 音視頻

  • 其他

C/C++

const

作用

  1. 修飾變量,說明該變量不可以被改變;

  2. 修飾指針,分為指向常量的指針和指針常量;

  3. 常量引用,經(jīng)常用于形參類型,即避免了拷貝,又避免了函數(shù)對值的修改;

  4. 修飾成員函數(shù),說明該成員函數(shù)內(nèi)不能修改成員變量。

使用

// 類
class A
{

private:
    const int a;                // 常對象成員,只能在初始化列表賦值

public:
    // 構(gòu)造函數(shù)
    A() { };
    A(int x) : a(x) { };        // 初始化列表

    // const可用于對重載函數(shù)的區(qū)分
    int getValue();             // 普通成員函數(shù)
    int getValue() const;       // 常成員函數(shù),不得修改類中的任何數(shù)據(jù)成員的值
};

void function()
{
    // 對象
    A b;                        // 普通對象,可以調(diào)用全部成員函數(shù)
    const A a;                  // 常對象,只能調(diào)用常成員函數(shù)、更新常成員變量
    const A *p = &a;            // 常指針
    const A &q = a;             // 常引用

    // 指針
    char greeting[] = 'Hello';
    char* p1 = greeting;                // 指針變量,指向字符數(shù)組變量
    const char* p2 = greeting;          // 指針變量,指向字符數(shù)組常量
    charconst p3 = greeting;          // 常指針,指向字符數(shù)組變量
    const charconst p4 = greeting;    // 常指針,指向字符數(shù)組常量
}

// 函數(shù)
void function1(const int Var);           // 傳遞過來的參數(shù)在函數(shù)內(nèi)不可變
void function2(const char* Var);         // 參數(shù)指針?biāo)竷?nèi)容為常量
void function3(charconst Var);         // 參數(shù)指針為常指針
void function4(const int& Var);          // 引用參數(shù)在函數(shù)內(nèi)為常量

// 函數(shù)返回值
const int function5();      // 返回一個常數(shù)
const intfunction6();     // 返回一個指向常量的指針變量,使用:const int *p = function6();
intconst function7();     // 返回一個指向變量的常指針,使用:int* const p = function7();

static

作用

  1. 修飾普通變量,修改變量的存儲區(qū)域和生命周期,使變量存儲在靜態(tài)區(qū),在 main 函數(shù)運行前就分配了空間,如果有初始值就用初始值初始化它,如果沒有初始值系統(tǒng)用默認(rèn)值初始化它。

  2. 修飾普通函數(shù),表明函數(shù)的作用范圍,僅在定義該函數(shù)的文件內(nèi)才能使用。在多人開發(fā)項目時,為了防止與他人命令函數(shù)重名,可以將函數(shù)定位為 static。

  3. 修飾成員變量,修飾成員變量使所有的對象只保存一個該變量,而且不需要生成對象就可以訪問該成員。

  4. 修飾成員函數(shù),修飾成員函數(shù)使得不需要生成對象就可以訪問該函數(shù),但是在 static 函數(shù)內(nèi)不能訪問非靜態(tài)成員。

this 指針

  1. this 指針是一個隱含于每一個非靜態(tài)成員函數(shù)中的特殊指針。它指向正在被該成員函數(shù)操作的那個對象。

  2. 當(dāng)對一個對象調(diào)用成員函數(shù)時,編譯程序先將對象的地址賦給 this 指針,然后調(diào)用成員函數(shù),每次成員函數(shù)存取數(shù)據(jù)成員時,由隱含使用 this 指針。

  3. 當(dāng)一個成員函數(shù)被調(diào)用時,自動向它傳遞一個隱含的參數(shù),該參數(shù)是一個指向這個成員函數(shù)所在的對象的指針。

  4. this 指針被隱含地聲明為: ClassName const this`,這意味著不能給 `this` 指針賦值;在 `ClassName` 類的 `const` 成員函數(shù)中,`this` 指針的類型為:`const ClassName const,這說明不能對 this 指針?biāo)赶虻倪@種對象是不可修改的(即不能對這種對象的數(shù)據(jù)成員進行賦值操作);

  5. this 并不是一個常規(guī)變量,而是個右值,所以不能取得 this 的地址(不能 &this)。

  6. 在以下場景中,經(jīng)常需要顯式引用 this 指針:

    1. 為實現(xiàn)對象的鏈?zhǔn)揭茫?/span>

    2. 為避免對同一對象進行賦值操作;

    3. 在實現(xiàn)一些數(shù)據(jù)結(jié)構(gòu)時,如 `list`。

inline 內(nèi)聯(lián)函數(shù)

特征

  • 相當(dāng)于把內(nèi)聯(lián)函數(shù)里面的內(nèi)容寫在調(diào)用內(nèi)聯(lián)函數(shù)處;

  • 相當(dāng)于不用執(zhí)行進入函數(shù)的步驟,直接執(zhí)行函數(shù)體;

  • 相當(dāng)于宏,卻比宏多了類型檢查,真正具有函數(shù)特性;

  • 不能包含循環(huán)、遞歸、switch 等復(fù)雜操作;

  • 在類聲明中定義的函數(shù),除了虛函數(shù)的其他函數(shù)都會自動隱式地當(dāng)成內(nèi)聯(lián)函數(shù)。

使用

// 聲明1(加 inline,建議使用)
inline int functionName(int first, int secend,...);

// 聲明2(不加 inline)
int functionName(int first, int secend,...);

// 定義
inline int functionName(int first, int secend,...) {/****/};

// 類內(nèi)定義,隱式內(nèi)聯(lián)
class A {
    int doA() return 0; }         // 隱式內(nèi)聯(lián)
}

// 類外定義,需要顯式內(nèi)聯(lián)
class A {
    int doA();
}
inline int A::doA() { return 0; }   // 需要顯式內(nèi)聯(lián)

編譯器對 inline 函數(shù)的處理步驟

  1. 將 inline 函數(shù)體復(fù)制到 inline 函數(shù)調(diào)用點處;

  2. 為所用 inline 函數(shù)中的局部變量分配內(nèi)存空間;

  3. 將 inline 函數(shù)的的輸入?yún)?shù)和返回值映射到調(diào)用方法的局部變量空間中;

  4. 如果 inline 函數(shù)有多個返回點,將其轉(zhuǎn)變?yōu)?inline 函數(shù)代碼塊末尾的分支(使用 GOTO)。

優(yōu)缺點

優(yōu)點

  1. 內(nèi)聯(lián)函數(shù)同宏函數(shù)一樣將在被調(diào)用處進行代碼展開,省去了參數(shù)壓棧、棧幀開辟與回收,結(jié)果返回等,從而提高程序運行速度。

  2. 內(nèi)聯(lián)函數(shù)相比宏函數(shù)來說,在代碼展開時,會做安全檢查或自動類型轉(zhuǎn)換(同普通函數(shù)),而宏定義則不會。

  3. 在類中聲明同時定義的成員函數(shù),自動轉(zhuǎn)化為內(nèi)聯(lián)函數(shù),因此內(nèi)聯(lián)函數(shù)可以訪問類的成員變量,宏定義則不能。

  4. 內(nèi)聯(lián)函數(shù)在運行時可調(diào)試,而宏定義不可以。

缺點

  1. 代碼膨脹。內(nèi)聯(lián)是以代碼膨脹(復(fù)制)為代價,消除函數(shù)調(diào)用帶來的開銷。如果執(zhí)行函數(shù)體內(nèi)代碼的時間,相比于函數(shù)調(diào)用的開銷較大,那么效率的收獲會很少。另一方面,每一處內(nèi)聯(lián)函數(shù)的調(diào)用都要復(fù)制代碼,將使程序的總代碼量增大,消耗更多的內(nèi)存空間。

  2. inline 函數(shù)無法隨著函數(shù)庫升級而升級。inline函數(shù)的改變需要重新編譯,不像 non-inline 可以直接鏈接。

  3. 是否內(nèi)聯(lián),程序員不可控。內(nèi)聯(lián)函數(shù)只是對編譯器的建議,是否對函數(shù)內(nèi)聯(lián),決定權(quán)在于編譯器。

虛函數(shù)(virtual)可以是內(nèi)聯(lián)函數(shù)(inline)嗎?

Are 'inline virtual' member functions ever actually 'inlined'?

答案:http://www.cs./users/yechiel/c++-faq/inline-virtuals.html

  • 虛函數(shù)可以是內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)是可以修飾虛函數(shù)的,但是當(dāng)虛函數(shù)表現(xiàn)多態(tài)性的時候不能內(nèi)聯(lián)。

  • 內(nèi)聯(lián)是在編譯器建議編譯器內(nèi)聯(lián),而虛函數(shù)的多態(tài)性在運行期,編譯器無法知道運行期調(diào)用哪個代碼,因此虛函數(shù)表現(xiàn)為多態(tài)性時(運行期)不可以內(nèi)聯(lián)。

  • inline virtual 唯一可以內(nèi)聯(lián)的時候是:編譯器知道所調(diào)用的對象是哪個類(如 Base::who()),這只有在編譯器具有實際對象而不是對象的指針或引用時才會發(fā)生。

虛函數(shù)內(nèi)聯(lián)使用

#include <iostream>  
using namespace std;
class Base
{

public:
    inline virtual void who()
    
{
        cout << 'I am Base\n';
    }
    virtual ~Base() {}
};
class Derived : public Base
{
public:
    inline void who()  // 不寫inline時隱式內(nèi)聯(lián)
    
{
        cout << 'I am Derived\n';
    }
};

int main()
{
    // 此處的虛函數(shù) who(),是通過類(Base)的具體對象(b)來調(diào)用的,編譯期間就能確定了,所以它可以是內(nèi)聯(lián)的,但最終是否內(nèi)聯(lián)取決于編譯器。 
    Base b;
    b.who();

    // 此處的虛函數(shù)是通過指針調(diào)用的,呈現(xiàn)多態(tài)性,需要在運行時期間才能確定,所以不能為內(nèi)聯(lián)。  
    Base *ptr = new Derived();
    ptr->who();

    // 因為Base有虛析構(gòu)函數(shù)(virtual ~Base() {}),所以 delete 時,會先調(diào)用派生類(Derived)析構(gòu)函數(shù),再調(diào)用基類(Base)析構(gòu)函數(shù),防止內(nèi)存泄漏。
    delete ptr;
    ptr = nullptr;

    system('pause');
    return 0;

assert()

斷言,是宏,而非函數(shù)。assert 宏的原型定義在 <assert.h>(C)、<cassert>(C++)中,其作用是如果它的條件返回錯誤,則終止程序執(zhí)行??梢酝ㄟ^定義 NDEBUG 來關(guān)閉 assert,但是需要在源代碼的開頭,include <assert.h> 之前。

使用

#define NDEBUG          // 加上這行,則 assert 不可用
#include <assert.h>

assert( p != NULL );    // assert 不可用

sizeof()

  • sizeof 對數(shù)組,得到整個數(shù)組所占空間大小。

  • sizeof 對指針,得到指針本身所占空間大小。

#pragma pack(n)

設(shè)定結(jié)構(gòu)體、聯(lián)合以及類成員變量以 n 字節(jié)方式對齊

使用

#pragma pack(push)  // 保存對齊狀態(tài)
#pragma pack(4)     // 設(shè)定為 4 字節(jié)對齊

struct test
{

    char m1;
    double m4;
    int m3;
};

#pragma pack(pop)   // 恢復(fù)對齊狀態(tài)

位域

Bit mode: 2;    // mode 占 2 位

類可以將其(非靜態(tài))數(shù)據(jù)成員定義為位域(bit-field),在一個位域中含有一定數(shù)量的二進制位。當(dāng)一個程序需要向其他程序或硬件設(shè)備傳遞二進制數(shù)據(jù)時,通常會用到位域。

  • 位域在內(nèi)存中的布局是與機器有關(guān)的

  • 位域的類型必須是整型或枚舉類型,帶符號類型中的位域的行為將因具體實現(xiàn)而定

  • 取地址運算符(&)不能作用于位域,任何指針都無法指向類的位域

volatile

volatile int i = 10
  • volatile 關(guān)鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素(操作系統(tǒng)、硬件、其它線程等)更改。所以使用 volatile 告訴編譯器不應(yīng)對這樣的對象進行優(yōu)化。

  • volatile 關(guān)鍵字聲明的變量,每次訪問時都必須從內(nèi)存中取出值(沒有被 volatile 修飾的變量,可能由于編譯器的優(yōu)化,從 CPU 寄存器中取值)

  • const 可以是 volatile (如只讀的狀態(tài)寄存器)

  • 指針可以是 volatile

extern 'C'

  • 被 extern 限定的函數(shù)或變量是 extern 類型的

  • extern 'C' 修飾的變量和函數(shù)是按照 C 語言方式編譯和連接的

extern 'C' 的作用是讓 C++ 編譯器將 extern 'C' 聲明的代碼當(dāng)作 C 語言代碼處理,可以避免 C++ 因符號修飾導(dǎo)致代碼不能和C語言庫中的符號進行鏈接的問題。

'C' 使用

#ifdef __cplusplus
extern 'C' {
#endif

void *memset(void *, intsize_t);

#ifdef __cplusplus
}
#endif

struct 和 typedef struct

C 中

// c
typedef struct Student {
    int age; 
} S;

等價于

// c
struct Student { 
    int age; 
};

typedef struct Student S;

此時 S 等價于 struct Student,但兩個標(biāo)識符名稱空間不相同。

另外還可以定義與 struct Student 不沖突的 void Student() {}。

C++ 中

由于編譯器定位符號的規(guī)則(搜索規(guī)則)改變,導(dǎo)致不同于C語言。

一、如果在類標(biāo)識符空間定義了 struct Student {...};,使用 Student me; 時,編譯器將搜索全局標(biāo)識符表,Student 未找到,則在類標(biāo)識符內(nèi)搜索。

即表現(xiàn)為可以使用 Student 也可以使用 struct Student,如下:

// cpp
struct Student { 
    int age; 
};

void f( Student me );       // 正確,'struct' 關(guān)鍵字可省略

二、若定義了與 Student 同名函數(shù)之后,則 Student 只代表函數(shù),不代表結(jié)構(gòu)體,如下:

typedef struct Student { 
    int age; 
} S;

void Student() {}           // 正確,定義后 'Student' 只代表此函數(shù)

//void S() {}               // 錯誤,符號 'S' 已經(jīng)被定義為一個 'struct Student' 的別名

int main() {
    Student(); 
    struct Student me;      // 或者 'S me';
    return 0;
}

C++ 中 struct 和 class

總的來說,struct 更適合看成是一個數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)體,class 更適合看成是一個對象的實現(xiàn)體。

區(qū)別

  • 最本質(zhì)的一個區(qū)別就是默認(rèn)的訪問控制

    1. 默認(rèn)的繼承訪問權(quán)限。struct 是 public 的,class 是 private 的。 

    2. struct 作為數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)體,它默認(rèn)的數(shù)據(jù)訪問控制是 public 的,而 class 作為對象的實現(xiàn)體,它默認(rèn)的成員變量訪問控制是 private 的。

union 聯(lián)合

聯(lián)合(union)是一種節(jié)省空間的特殊的類,一個 union 可以有多個數(shù)據(jù)成員,但是在任意時刻只有一個數(shù)據(jù)成員可以有值。當(dāng)某個成員被賦值后其他成員變?yōu)槲炊x狀態(tài)。聯(lián)合有如下特點:

  • 默認(rèn)訪問控制符為 public

  • 可以含有構(gòu)造函數(shù)、析構(gòu)函數(shù)

  • 不能含有引用類型的成員

  • 不能繼承自其他類,不能作為基類

  • 不能含有虛函數(shù)

  • 匿名 union 在定義所在作用域可直接訪問 union 成員

  • 匿名 union 不能包含 protected 成員或 private 成員

  • 全局匿名聯(lián)合必須是靜態(tài)(static)的

    使用

#include<iostream>

union UnionTest {
    UnionTest() : i(10) {};
    int i;
    double d;
};

static union {
    int i;
    double d;
};

int main() {
    UnionTest u;

    union {
        int i;
        double d;
    };

    std::cout << u.i << std::endl;  // 輸出 UnionTest 聯(lián)合的 10

    ::i = 20;
    std::cout << ::i << std::endl;  // 輸出全局靜態(tài)匿名聯(lián)合的 20

    i = 30;
    std::cout << i << std::endl;    // 輸出局部匿名聯(lián)合的 30

    return 0;
}

C 實現(xiàn) C++ 類

C 語言實現(xiàn)封裝、繼承和多態(tài):

http:///cpp/ooc/

explicit(顯式)構(gòu)造函數(shù)

explicit 修飾的構(gòu)造函數(shù)可用來防止隱式轉(zhuǎn)換

explicit 使用

class Test1
{

public:
    Test1(int n)            // 普通構(gòu)造函數(shù)
    {
        num=n;
    }
private:
    int num;
};

class Test2
{

public:
    explicit Test2(int n)   // explicit(顯式)構(gòu)造函數(shù)
    
{
        num=n;
    }
private:
    int num;
};

int main()
{
    Test1 t1=12;            // 隱式調(diào)用其構(gòu)造函數(shù),成功
    Test2 t2=12;            // 編譯錯誤,不能隱式調(diào)用其構(gòu)造函數(shù)
    Test2 t2(12);           // 顯式調(diào)用成功
    return 0;
}

friend 友元類和友元函數(shù)

  • 能訪問私有成員  

  • 破壞封裝性

  • 友元關(guān)系不可傳遞

  • 友元關(guān)系的單向性

  • 友元聲明的形式及數(shù)量不受限制

using

using 聲明

一條 using 聲明 語句一次只引入命名空間的一個成員。它使得我們可以清楚知道程序中所引用的到底是哪個名字。如:

using namespace_name::name;

構(gòu)造函數(shù)的 using 聲明【C++11】

在 C++11 中,派生類能夠重用其直接基類定義的構(gòu)造函數(shù)。

class Derived : Base {
public:
    using Base::Base;
    /* ... */
};

如上 using 聲明,對于基類的每個構(gòu)造函數(shù),編譯器都生成一個與之對應(yīng)(形參列表完全相同)的派生類構(gòu)造函數(shù)。生成如下類型構(gòu)造函數(shù):

derived(parms) : base(args) { }

using 指示

using 指示 使得某個特定命名空間中所有名字都可見,這樣我們就無需再為它們添加任何前綴限定符了。如:

using namespace_name name;

盡量少使用 `using 指示` 污染命名空間

一般說來,使用 using 命令比使用 using 編譯命令更安全,這是由于它只導(dǎo)入了制定的名稱。如果該名稱與局部名稱發(fā)生沖突,編譯器將發(fā)出指示。using編譯命令導(dǎo)入所有的名稱,包括可能并不需要的名稱。如果與局部名稱發(fā)生沖突,則局部名稱將覆蓋名稱空間版本,而編譯器并不會發(fā)出警告。另外,名稱空間的開放性意味著名稱空間的名稱可能分散在多個地方,這使得難以準(zhǔn)確知道添加了哪些名稱。

using 使用

盡量少使用 using 指示

using namespace std;

應(yīng)該多使用 using 聲明

int x;
std::cin >> x ;
std::cout << x << std::endl;

或者

using std::cin;
using std::cout;
using std::endl;
int x;
cin >> x;
cout << x << endl;

:: 范圍解析運算符

分類

  1. 全局作用域符(::name):用于類型名稱(類、類成員、成員函數(shù)、變量等)前,表示作用域為全局命名空間

  2. 類作用域符(class::name):用于表示指定類型的作用域范圍是具體某個類的

  3. 命名空間作用域符(namespace::name):用于表示指定類型的作用域范圍是具體某個命名空間的

:: 使用

int count = 0;        // 全局(::)的 count

class A {
public:
    static int count; // 類 A 的 count(A::count)
};

int main() {
    ::count = 1;      // 設(shè)置全局的 count 的值為 1

    A::count = 2;     // 設(shè)置類 A 的 count 為 2

    int count = 0;    // 局部的 count
    count = 3;        // 設(shè)置局部的 count 的值為 3

    return 0;
}

enum 枚舉類型

限定作用域的枚舉類型

enum class open_modes { input, output, append };

不限定作用域的枚舉類型

enum color { red, yellow, green };
enum { floatPrec = 6, doublePrec = 10 };

decltype

decltype 關(guān)鍵字用于檢查實體的聲明類型或表達(dá)式的類型及值分類。語法:

decltype ( expression )

使用

// 尾置返回允許我們在參數(shù)列表之后聲明返回類型
template <typename It>
auto fcn(It beg, It end) -> decltype(*beg)
{
    // 處理序列
    return *beg;    // 返回序列中一個元素的引用
}
// 為了使用模板參數(shù)成員,必須用 typename
template <typename It>
auto fcn2(It beg, It end) -> typename remove_reference<decltype(*beg)>::type
{
    // 處理序列
    return *beg;    // 返回序列中一個元素的拷貝
}

引用

左值引用

常規(guī)引用,一般表示對象的身份。

右值引用

右值引用就是必須綁定到右值(一個臨時對象、將要銷毀的對象)的引用,一般表示對象的值。

右值引用可實現(xiàn)轉(zhuǎn)移語義(Move Sementics)和精確傳遞(Perfect Forwarding),它的主要目的有兩個方面:

  • 消除兩個對象交互時不必要的對象拷貝,節(jié)省運算存儲資源,提高效率。

  • 能夠更簡潔明確地定義泛型函數(shù)。

引用折疊

  • X& &、X& &&、X&& & 可折疊成 X&

  • X&& && 可折疊成 X&&

  • 宏定義可以實現(xiàn)類似于函數(shù)的功能,但是它終歸不是函數(shù),而宏定義中括弧中的“參數(shù)”也不是真的參數(shù),在宏展開的時候?qū)?“參數(shù)” 進行的是一對一的替換。

成員初始化列表

好處

  • 更高效:少了一次調(diào)用默認(rèn)構(gòu)造函數(shù)的過程。

  • 有些場合必須要用初始化列表:

  1. 常量成員,因為常量只能初始化不能賦值,所以必須放在初始化列表里面

  2. 引用類型,引用必須在定義的時候初始化,并且不能重新賦值,所以也要寫在初始化列表里面

  3. 沒有默認(rèn)構(gòu)造函數(shù)的類類型,因為使用初始化列表可以不必調(diào)用默認(rèn)構(gòu)造函數(shù)來初始化,而是直接調(diào)用拷貝構(gòu)造函數(shù)初始化。

initializer_list 列表初始化【C++11】

用花括號初始化器列表列表初始化一個對象,其中對應(yīng)構(gòu)造函數(shù)接受一個 std::initializer_list 參數(shù).

initializer_list 使用

#include <iostream>
#include <vector>
#include <initializer_list>

template <class T>
struct S {

    std::vector<T> v;
    S(std::initializer_list<T> l) : v(l) {
         std::cout << 'constructed with a ' << l.size() << '-element list\n';
    }
    void append(std::initializer_list<T> l) {
        v.insert(v.end(), l.begin(), l.end());
    }
    std::pair<const T*, std::size_t> c_arr() const {
        return {&v[0], v.size()};  // 在 return 語句中復(fù)制列表初始化
                                   // 這不使用 std::initializer_list
    }
};

template <typename T>
void templated_fn(T) {}

int main()
{
    S<int> s = {12345}; // 復(fù)制初始化
    s.append({678});      // 函數(shù)調(diào)用中的列表初始化

    std::cout << 'The vector size is now ' << s.c_arr().second << ' ints:\n';

    for (auto n : s.v)
        std::cout << n << ' ';
    std::cout << '\n';

    std::cout << 'Range-for over brace-init-list: \n';

    for (int x : {-1-2-3}) // auto 的規(guī)則令此帶范圍 for 工作
        std::cout << x << ' ';
    std::cout << '\n';

    auto al = {101112};   // auto 的特殊規(guī)則

    std::cout << 'The list bound to auto has size() = ' << al.size() << '\n';

//    templated_fn({1, 2, 3}); // 編譯錯誤!“ {1, 2, 3} ”不是表達(dá)式,
                             // 它無類型,故 T 無法推導(dǎo)
    templated_fn<std::initializer_list<int>>({123}); // OK
    templated_fn<std::vector<int>>({123});           // 也 OK
}

面向?qū)ο?/span>

面向?qū)ο蟪绦蛟O(shè)計(Object-oriented programming,OOP)是種具有對象概念的程序編程典范,同時也是一種程序開發(fā)的抽象方針。

面向?qū)ο筇卣?/figcaption>

面向?qū)ο笕筇卣?—— 封裝、繼承、多態(tài)

封裝

  • 把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。

  • 關(guān)鍵字:public, protected, friendly, private。不寫默認(rèn)為 friendly。

關(guān)鍵字當(dāng)前類包內(nèi)子孫類包外
public
protected×
friendly××
private×××

繼承

  • 基類(父類)——> 派生類(子類)

多態(tài)

  • 多態(tài),即多種狀態(tài),在面向?qū)ο笳Z言中,接口的多種不同的實現(xiàn)方式即為多態(tài)。

  • C++ 多態(tài)有兩種:靜態(tài)多態(tài)(早綁定)、動態(tài)多態(tài)(晚綁定)。靜態(tài)多態(tài)是通過函數(shù)重載實現(xiàn)的;動態(tài)多態(tài)是通過虛函數(shù)實現(xiàn)的。

  • 多態(tài)是以封裝和繼承為基礎(chǔ)的。

靜態(tài)多態(tài)(早綁定)

函數(shù)重載

class A
{

public:
    void do(int a);
    void do(int a, int b);
};

動態(tài)多態(tài)(晚綁定)

  • 虛函數(shù):用 virtual 修飾成員函數(shù),使其成為虛函數(shù)

注意:

  • 普通函數(shù)(非類成員函數(shù))不能是虛函數(shù)

  • 靜態(tài)函數(shù)(static)不能是虛函數(shù)

  • 構(gòu)造函數(shù)不能是虛函數(shù)(因為在調(diào)用構(gòu)造函數(shù)時,虛表指針并沒有在對象的內(nèi)存空間中,必須要構(gòu)造函數(shù)調(diào)用完成后才會形成虛表指針)

  • 內(nèi)聯(lián)函數(shù)不能是表現(xiàn)多態(tài)性時的虛函數(shù),解釋見:虛函數(shù)(virtual)可以是內(nèi)聯(lián)函數(shù)(inline)嗎?:http:///E4WVXSP

動態(tài)多態(tài)使用

class Shape                     // 形狀類
{

public:
    virtual double calcArea()
    
{
        ...
    }
    virtual ~Shape();
};
class Circle : public Shape     // 圓形類
{
public:
    virtual double calcArea();
    ...
};
class Rect : public Shape       // 矩形類
{
public:
    virtual double calcArea();
    ...
};
int main()
{
    Shape * shape1 = new Circle(4.0);
    Shape * shape2 = new Rect(5.06.0);
    shape1->calcArea();         // 調(diào)用圓形類里面的方法
    shape2->calcArea();         // 調(diào)用矩形類里面的方法
    delete shape1;
    shape1 = nullptr;
    delete shape2;
    shape2 = nullptr;
    return 0;
}

虛析構(gòu)函數(shù)

虛析構(gòu)函數(shù)是為了解決基類的指針指向派生類對象,并用基類的指針刪除派生類對象。

虛析構(gòu)函數(shù)使用

class Shape
{

public:
    Shape();                    // 構(gòu)造函數(shù)不能是虛函數(shù)
    virtual double calcArea();
    virtual ~Shape();           // 虛析構(gòu)函數(shù)
};
class Circle : public Shape     // 圓形類
{
public:
    virtual double calcArea();
    ...
};
int main()
{
    Shape * shape1 = new Circle(4.0);
    shape1->calcArea();    
    delete shape1;  // 因為Shape有虛析構(gòu)函數(shù),所以delete釋放內(nèi)存時,先調(diào)用子類析構(gòu)函數(shù),再調(diào)用基類析構(gòu)函數(shù),防止內(nèi)存泄漏。
    shape1 = NULL;
    return 0;
}

純虛函數(shù)

純虛函數(shù)是一種特殊的虛函數(shù),在基類中不能對虛函數(shù)給出有意義的實現(xiàn),而把它聲明為純虛函數(shù),它的實現(xiàn)留給該基類的派生類去做。

virtual int A() 0;

虛函數(shù)、純虛函數(shù)

CSDN . C++ 中的虛函數(shù)、純虛函數(shù)區(qū)別和聯(lián)系:http:///E4WVQBI

  • 類里如果聲明了虛函數(shù),這個函數(shù)是實現(xiàn)的,哪怕是空實現(xiàn),它的作用就是為了能讓這個函數(shù)在它的子類里面可以被覆蓋,這樣的話,這樣編譯器就可以使用后期綁定來達(dá)到多態(tài)了。純虛函數(shù)只是一個接口,是個函數(shù)的聲明而已,它要留到子類里去實現(xiàn)。

  • 虛函數(shù)在子類里面也可以不重載的;但純虛函數(shù)必須在子類去實現(xiàn)。

  • 虛函數(shù)的類用于 “實作繼承”,繼承接口的同時也繼承了父類的實現(xiàn)。當(dāng)然大家也可以完成自己的實現(xiàn)。純虛函數(shù)關(guān)注的是接口的統(tǒng)一性,實現(xiàn)由子類完成。

  • 帶純虛函數(shù)的類叫抽象類,這種類不能直接生成對象,而只有被繼承,并重寫其虛函數(shù)后,才能使用。抽象類和大家口頭常說的虛基類還是有區(qū)別的,在 C# 中用 abstract 定義抽象類,而在 C++ 中有抽象類的概念,但是沒有這個關(guān)鍵字。抽象類被繼承后,子類可以繼續(xù)是抽象類,也可以是普通類,而虛基類,是含有純虛函數(shù)的類,它如果被繼承,那么子類就必須實現(xiàn)虛基類里面的所有純虛函數(shù),其子類不能是抽象類。

虛函數(shù)指針、虛函數(shù)表

  • 虛函數(shù)指針:在含有虛函數(shù)類的對象中,指向虛函數(shù)表,在運行時確定。

  • 虛函數(shù)表:在程序只讀數(shù)據(jù)段(.rodata section,見:目標(biāo)文件存儲結(jié)構(gòu):http:///E4WVBeF),存放虛函數(shù)指針,如果派生類實現(xiàn)了基類的某個虛函數(shù),則在虛表中覆蓋原本基類的那個虛函數(shù)指針,在編譯時根據(jù)類的聲明創(chuàng)建。

虛繼承

虛繼承用于解決多繼承條件下的菱形繼承問題(浪費存儲空間、存在二義性)。

底層實現(xiàn)原理與編譯器相關(guān),一般通過虛基類指針虛基類表實現(xiàn),每個虛繼承的子類都有一個虛基類指針(占用一個指針的存儲空間,4字節(jié))和虛基類表(不占用類對象的存儲空間)(需要強調(diào)的是,虛基類依舊會在子類里面存在拷貝,只是僅僅最多存在一份而已,并不是不在子類里面了);當(dāng)虛繼承的子類被當(dāng)做父類繼承時,虛基類指針也會被繼承。

實際上,vbptr 指的是虛基類表指針(virtual base table pointer),該指針指向了一個虛基類表(virtual table),虛表中記錄了虛基類與本類的偏移地址;通過偏移地址,這樣就找到了虛基類成員,而虛繼承也不用像普通多繼承那樣維持著公共基類(虛基類)的兩份同樣的拷貝,節(jié)省了存儲空間。

虛繼承、虛函數(shù)

  • 相同之處:都利用了虛指針(均占用類的存儲空間)和虛表(均不占用類的存儲空間)

  • 不同之處:

    • 虛函數(shù)不占用存儲空間

    • 虛函數(shù)表存儲的是虛函數(shù)地址

    • 虛基類依舊存在繼承類中,只占用存儲空間

    • 虛基類表存儲的是虛基類相對直接繼承類的偏移

    • 虛繼承

    • 虛函數(shù)

模板類、成員模板、虛函數(shù)

  • 模板類中可以使用虛函數(shù)

  • 一個類(無論是普通類還是類模板)的成員模板(本身是模板的成員函數(shù))不能是虛函數(shù)

抽象類、接口類、聚合類

  • 抽象類:含有純虛函數(shù)的類

  • 接口類:僅含有純虛函數(shù)的抽象類

  • 聚合類:用戶可以直接訪問其成員,并且具有特殊的初始化語法形式。滿足如下特點:

    • 所有成員都是 public

    • 沒有有定于任何構(gòu)造函數(shù)

    • 沒有類內(nèi)初始化

    • 沒有基類,也沒有 virtual 函數(shù)

內(nèi)存分配和管理

malloc、calloc、realloc、alloca

  1. malloc:申請指定字節(jié)數(shù)的內(nèi)存。申請到的內(nèi)存中的初始值不確定。

  2. calloc:為指定長度的對象,分配能容納其指定個數(shù)的內(nèi)存。申請到的內(nèi)存的每一位(bit)都初始化為 0。

  3. realloc:更改以前分配的內(nèi)存長度(增加或減少)。當(dāng)增加長度時,可能需將以前分配區(qū)的內(nèi)容移到另一個足夠大的區(qū)域,而新增區(qū)域內(nèi)的初始值則不確定。

  4. alloca:在棧上申請內(nèi)存。程序在出棧的時候,會自動釋放內(nèi)存。但是需要注意的是,alloca 不具可移植性, 而且在沒有傳統(tǒng)堆棧的機器上很難實現(xiàn)。alloca 不宜使用在必須廣泛移植的程序中。C99 中支持變長數(shù)組 (VLA),可以用來替代 alloca。

malloc、free

用于分配、釋放內(nèi)存

malloc、free 使用

申請內(nèi)存,確認(rèn)是否申請成功

char *str = (char*) malloc(100);
assert(str != nullptr);

釋放內(nèi)存后指針置空

free(p); 
p = nullptr;

new、delete

  1. new / new[]:完成兩件事,先底層調(diào)用 malloc 分了配內(nèi)存,然后調(diào)用構(gòu)造函數(shù)(創(chuàng)建對象)。

  2. delete/delete[]:也完成兩件事,先調(diào)用析構(gòu)函數(shù)(清理資源),然后底層調(diào)用 free 釋放空間。

  3. new 在申請內(nèi)存時會自動計算所需字節(jié)數(shù),而 malloc 則需我們自己輸入申請內(nèi)存空間的字節(jié)數(shù)。

new、delete 使用

申請內(nèi)存,確認(rèn)是否申請成功

int main()
{
    T* t = new T();     // 先內(nèi)存分配 ,再構(gòu)造函數(shù)
    delete t;           // 先析構(gòu)函數(shù),再內(nèi)存釋放
    return 0;
}

定位 new

定位 new(placement new)允許我們向 new 傳遞額外的參數(shù)。

new (palce_address) type
new (palce_address) type (initializers)
new (palce_address) type [size]
new (palce_address) type [size] { braced initializer list }
  • palce_address 是個指針

  • initializers 提供一個(可能為空的)以逗號分隔的初始值列表

delete this 合法嗎?

Is it legal (and moral) for a member function to say delete this?
答案:http:///E4Wfcfl

合法,但:

  1. 必須保證 this 對象是通過 new(不是 new[]、不是 placement new、不是棧上、不是全局、不是其他對象成員)分配的

  2. 必須保證調(diào)用 delete this 的成員函數(shù)是最后一個調(diào)用 this 的成員函數(shù)

  3. 必須保證成員函數(shù)的 delete this 后面沒有調(diào)用 this 了

  4. 必須保證 delete this 后沒有人使用了

如何定義一個只能在堆上(棧上)生成對象的類?

如何定義一個只能在堆上(棧上)生成對象的類?

答案:http:///E4WfDhP

只能在堆上

方法:將析構(gòu)函數(shù)設(shè)置為私有

原因:C++ 是靜態(tài)綁定語言,編譯器管理棧上對象的生命周期,編譯器在為類對象分配??臻g時,會先檢查類的析構(gòu)函數(shù)的訪問性。若析構(gòu)函數(shù)不可訪問,則不能在棧上創(chuàng)建對象。

只能在棧上

方法:將 new 和 delete 重載為私有

原因:在堆上生成對象,使用 new 關(guān)鍵詞操作,其過程分為兩階段:第一階段,使用 new 在堆上尋找可用內(nèi)存,分配給對象;第二階段,調(diào)用構(gòu)造函數(shù)生成對象。將 new 操作設(shè)置為私有,那么第一階段就無法完成,就不能夠在堆上生成對象。

智能指針

C++ 標(biāo)準(zhǔn)庫(STL)中

頭文件:#include <memory>

C++ 98

std::auto_ptr<std::string> ps (new std::string(str));

C++ 11

  1. shared_ptr

  2. unique_ptr

  3. weak_ptr

  4. auto_ptr(被 C++11 棄用)

  • Class shared_ptr 實現(xiàn)共享式擁有(shared ownership)概念。多個智能指針指向相同對象,該對象和其相關(guān)資源會在 “最后一個 reference 被銷毀” 時被釋放。為了在結(jié)構(gòu)較復(fù)雜的情景中執(zhí)行上述工作,標(biāo)準(zhǔn)庫提供 weak_ptr、bad_weak_ptr 和 enable_shared_from_this 等輔助類。

  • Class unique_ptr 實現(xiàn)獨占式擁有(exclusive ownership)或嚴(yán)格擁有(strict ownership)概念,保證同一時間內(nèi)只有一個智能指針可以指向該對象。你可以移交擁有權(quán)。它對于避免內(nèi)存泄漏(resource leak)——如 new 后忘記 delete ——特別有用。

shared_ptr

多個智能指針可以共享同一個對象,對象的最末一個擁有著有責(zé)任銷毀對象,并清理與該對象相關(guān)的所有資源。

  • 支持定制型刪除器(custom deleter),可防范 Cross-DLL 問題(對象在動態(tài)鏈接庫(DLL)中被 new 創(chuàng)建,卻在另一個 DLL 內(nèi)被 delete 銷毀)、自動解除互斥鎖

weak_ptr

weak_ptr 允許你共享但不擁有某對象,一旦最末一個擁有該對象的智能指針失去了所有權(quán),任何 weak_ptr 都會自動成空(empty)。因此,在 default 和 copy 構(gòu)造函數(shù)之外,weak_ptr 只提供 “接受一個 shared_ptr” 的構(gòu)造函數(shù)。

  • 可打破環(huán)狀引用(cycles of references,兩個其實已經(jīng)沒有被使用的對象彼此互指,使之看似還在 “被使用” 的狀態(tài))的問題

unique_ptr

unique_ptr 是 C++11 才開始提供的類型,是一種在異常時可以幫助避免資源泄漏的智能指針。采用獨占式擁有,意味著可以確保一個對象和其相應(yīng)的資源同一時間只被一個 pointer 擁有。一旦擁有著被銷毀或編程 empty,或開始擁有另一個對象,先前擁有的那個對象就會被銷毀,其任何相應(yīng)資源亦會被釋放。

  • unique_ptr 用于取代 auto_ptr

auto_ptr

被 c++11 棄用,原因是缺乏語言特性如 “針對構(gòu)造和賦值” 的 std::move 語義,以及其他瑕疵。

auto_ptr 與 unique_ptr 比較
  • auto_ptr 可以賦值拷貝,復(fù)制拷貝后所有權(quán)轉(zhuǎn)移;unqiue_ptr 無拷貝賦值語義,但實現(xiàn)了move 語義;

  • auto_ptr 對象不能管理數(shù)組(析構(gòu)調(diào)用 delete),unique_ptr 可以管理數(shù)組(析構(gòu)調(diào)用 delete[] );

強制類型轉(zhuǎn)換運算符

MSDN . 強制轉(zhuǎn)換運算符:http:///E4WIt5W

static_cast

  • 用于非多態(tài)類型的轉(zhuǎn)換

  • 不執(zhí)行運行時類型檢查(轉(zhuǎn)換安全性不如 dynamic_cast)

  • 通常用于轉(zhuǎn)換數(shù)值數(shù)據(jù)類型(如 float -> int)

  • 可以在整個類層次結(jié)構(gòu)中移動指針,子類轉(zhuǎn)化為父類安全(向上轉(zhuǎn)換),父類轉(zhuǎn)化為子類不安全(因為子類可能有不在父類的字段或方法)

向上轉(zhuǎn)換是一種隱式轉(zhuǎn)換。

dynamic_cast

  • 用于多態(tài)類型的轉(zhuǎn)換

  • 執(zhí)行行運行時類型檢查

  • 只適用于指針或引用

  • 對不明確的指針的轉(zhuǎn)換將失敗(返回 nullptr),但不引發(fā)異常

  • 可以在整個類層次結(jié)構(gòu)中移動指針,包括向上轉(zhuǎn)換、向下轉(zhuǎn)換

const_cast

  • 用于刪除 const、volatile 和 __unaligned 特性(如將 const int 類型轉(zhuǎn)換為 int 類型 )

reinterpret_cast

  • 用于位的簡單重新解釋

  • 濫用 reinterpret_cast 運算符可能很容易帶來風(fēng)險。除非所需轉(zhuǎn)換本身是低級別的,否則應(yīng)使用其他強制轉(zhuǎn)換運算符之一。

  • 允許將任何指針轉(zhuǎn)換為任何其他指針類型(如 char` 到 `intOne_class` 到 `Unrelated_class 之類的轉(zhuǎn)換,但其本身并不安全)

  • 也允許將任何整數(shù)類型轉(zhuǎn)換為任何指針類型以及反向轉(zhuǎn)換。

  • reinterpret_cast 運算符不能丟掉 const、volatile 或 __unaligned 特性。

  • reinterpret_cast 的一個實際用途是在哈希函數(shù)中,即,通過讓兩個不同的值幾乎不以相同的索引結(jié)尾的方式將值映射到索引。

bad_cast

  • 由于強制轉(zhuǎn)換為引用類型失敗,dynamic_cast 運算符引發(fā) bad_cast 異常。

bad_cast 使用

try {  
    Circle& ref_circle = dynamic_cast<Circle&>(ref_shape);   
}  
catch (bad_cast b) {  
    cout << 'Caught: ' << b.what();  

運行時類型信息 (RTTI)

dynamic_cast

  • 用于多態(tài)類型的轉(zhuǎn)換

typeid

  • typeid 運算符允許在運行時確定對象的類型

  • type_id 返回一個 type_info 對象的引用

  • 如果想通過基類的指針獲得派生類的數(shù)據(jù)類型,基類必須帶有虛函數(shù)

  • 只能獲取對象的實際類型

type_info

  • type_info 類描述編譯器在程序中生成的類型信息。此類的對象可以有效存儲指向類型的名稱的指針。type_info 類還可存儲適合比較兩個類型是否相等或比較其排列順序的編碼值。類型的編碼規(guī)則和排列順序是未指定的,并且可能因程序而異。

  • 頭文件:typeinfo

typeid、type_info 使用

class Flyable                       // 能飛的
{

public:
    virtual void takeoff() 0;     // 起飛
    virtual void land() 0;        // 降落
};
class Bird : public Flyable         // 鳥
{
public:
    void foraging() {...}           // 覓食
    virtual void takeoff() {...}
    virtual void land() {...}
};
class Plane : public Flyable        // 飛機
{
public:
    void carry() {...}              // 運輸
    virtual void take off() {...}
    virtual void land() {...}
};

class type_info
{

public:
    const charname() const;
    bool operator == (const type_info & rhs) const;
    bool operator != (const type_info & rhs) const;
    int before(const type_info & rhs) const;
    virtual ~type_info();
private:
    ...
};

class doSomething(Flyable *obj)                 // 做些事情
{
    obj->takeoff();

    cout << typeid(*obj).name() << endl;        // 輸出傳入對象類型('class Bird' or 'class Plane')

    if(typeid(*obj) == typeid(Bird))            // 判斷對象類型
    {
        Bird *bird = dynamic_cast<Bird *>(obj); // 對象轉(zhuǎn)化
        bird->foraging();
    }

    obj->land();
};

Effective C++

  1. 視 C++ 為一個語言聯(lián)邦(C、Object-Oriented C++、Template C++、STL)

  2. 寧可以編譯器替換預(yù)處理器(盡量以 const、enum、inline 替換 #define

  3. 盡可能使用 const

  4. 確定對象被使用前已先被初始化(構(gòu)造時賦值(copy 構(gòu)造函數(shù))比 default 構(gòu)造后賦值(copy assignment)效率高)

  5. 了解 C++ 默默編寫并調(diào)用哪些函數(shù)(編譯器暗自為 class 創(chuàng)建 default 構(gòu)造函數(shù)、copy 構(gòu)造函數(shù)、copy assignment 操作符、析構(gòu)函數(shù))

  6. 若不想使用編譯器自動生成的函數(shù),就應(yīng)該明確拒絕(將不想使用的成員函數(shù)聲明為 private,并且不予實現(xiàn))

  7. 為多態(tài)基類聲明 virtual 析構(gòu)函數(shù)(如果 class 帶有任何 virtual 函數(shù),它就應(yīng)該擁有一個 virtual 析構(gòu)函數(shù))

  8. 別讓異常逃離析構(gòu)函數(shù)(析構(gòu)函數(shù)應(yīng)該吞下不傳播異常,或者結(jié)束程序,而不是吐出異常;如果要處理異常應(yīng)該在非析構(gòu)的普通函數(shù)處理)

  9. 絕不在構(gòu)造和析構(gòu)過程中調(diào)用 virtual 函數(shù)(因為這類調(diào)用從不下降至 derived class)

  10. operator= 返回一個 reference to *this (用于連鎖賦值)

  11. operator= 中處理 “自我賦值”

  12. 賦值對象時應(yīng)確保復(fù)制 “對象內(nèi)的所有成員變量” 及 “所有 base class 成分”(調(diào)用基類復(fù)制構(gòu)造函數(shù))

  13. 以對象管理資源(資源在構(gòu)造函數(shù)獲得,在析構(gòu)函數(shù)釋放,建議使用智能指針,資源取得時機便是初始化時機(Resource Acquisition Is Initialization,RAII))

  14. 在資源管理類中小心 copying 行為(普遍的 RAII class copying 行為是:抑制 copying、引用計數(shù)、深度拷貝、轉(zhuǎn)移底部資源擁有權(quán)(類似 auto_ptr))

  15. 在資源管理類中提供對原始資源(raw resources)的訪問(對原始資源的訪問可能經(jīng)過顯式轉(zhuǎn)換或隱式轉(zhuǎn)換,一般而言顯示轉(zhuǎn)換比較安全,隱式轉(zhuǎn)換對客戶比較方便)

  16. 成對使用 new 和 delete 時要采取相同形式(new 中使用 []delete [],new 中不使用 []delete

  17. 以獨立語句將 newed 對象存儲于(置入)智能指針(如果不這樣做,可能會因為編譯器優(yōu)化,導(dǎo)致難以察覺的資源泄漏)

  18. 讓接口容易被正確使用,不易被誤用(促進正常使用的辦法:接口的一致性、內(nèi)置類型的行為兼容;阻止誤用的辦法:建立新類型,限制類型上的操作,約束對象值、消除客戶的資源管理責(zé)任)

  19. 設(shè)計 class 猶如設(shè)計 type,需要考慮對象創(chuàng)建、銷毀、初始化、賦值、值傳遞、合法值、繼承關(guān)系、轉(zhuǎn)換、一般化等等。

  20. 寧以 pass-by-reference-to-const 替換 pass-by-value (前者通常更高效、避免切割問題(slicing problem),但不適用于內(nèi)置類型、STL迭代器、函數(shù)對象)

  21. 必須返回對象時,別妄想返回其 reference(絕不返回 pointer 或 reference 指向一個 local stack 對象,或返回 reference 指向一個 heap-allocated 對象,或返回 pointer 或 reference 指向一個 local static 對象而有可能同時需要多個這樣的對象。)

  22. 將成員變量聲明為 private(為了封裝、一致性、對其讀寫精確控制等)

  23. 寧以 non-member、non-friend 替換 member 函數(shù)(可增加封裝性、包裹彈性(packaging flexibility)、機能擴充性)

  24. 若所有參數(shù)(包括被this指針?biāo)傅哪莻€隱喻參數(shù))皆須要類型轉(zhuǎn)換,請為此采用 non-member 函數(shù)

  25. 考慮寫一個不拋異常的 swap 函數(shù)

  26. 盡可能延后變量定義式的出現(xiàn)時間(可增加程序清晰度并改善程序效率)

  27. 盡量少做轉(zhuǎn)型動作(舊式:(T)expression、T(expression);新式:const_cast(expression)dynamic_cast(expression)、reinterpret_cast(expression)static_cast(expression)、;盡量避免轉(zhuǎn)型、注重效率避免 dynamic_casts、盡量設(shè)計成無需轉(zhuǎn)型、可把轉(zhuǎn)型封裝成函數(shù)、寧可用新式轉(zhuǎn)型)

  28. 避免使用 handles(包括 引用、指針、迭代器)指向?qū)ο髢?nèi)部(以增加封裝性、使 const 成員函數(shù)的行為更像 const、降低 “虛吊號碼牌”(dangling handles,如懸空指針等)的可能性)

  29. 為 “異常安全” 而努力是值得的(異常安全函數(shù)(Exception-safe functions)即使發(fā)生異常也不會泄露資源或允許任何數(shù)據(jù)結(jié)構(gòu)敗壞,分為三種可能的保證:基本型、強列型、不拋異常型)

  30. 透徹了解 inlining 的里里外外(inlining 在大多數(shù) C++ 程序中是編譯期的行為;inline 函數(shù)是否真正 inline,取決于編譯器;大部分編譯器拒絕太過復(fù)雜(如帶有循環(huán)或遞歸)的函數(shù) inlining,而所有對 virtual 函數(shù)的調(diào)用(除非是最平淡無奇的)也都會使 inlining 落空;inline 造成的代碼膨脹可能帶來效率損失;inline 函數(shù)無法隨著程序庫的升級而升級)

  31. 將文件間的編譯依存關(guān)系降至最低(如果使用 object references 或 object pointers 可以完成任務(wù),就不要使用 objects;如果能過夠,盡量以 class 聲明式替換 class 定義式;為聲明式和定義式提供不同的頭文件)

  32. 確定你的 public 繼承塑模出 is-a(是一種)關(guān)系(適用于 base classes 身上的每一件事情一定適用于 derived classes 身上,因為每一個 derived class 對象也都是一個 base class 對象)

  33. 避免遮掩繼承而來的名字(可使用 using 聲明式或轉(zhuǎn)交函數(shù)(forwarding functions)來讓被遮掩的名字再見天日)

  34. 區(qū)分接口繼承和實現(xiàn)繼承(在 public 繼承之下,derived classes 總是繼承 base class 的接口;pure virtual 函數(shù)只具體指定接口繼承;非純 impure virtual 函數(shù)具體指定接口繼承及缺省實現(xiàn)繼承;non-virtual 函數(shù)具體指定接口繼承以及強制性實現(xiàn)繼承)

  35. 考慮 virtual 函數(shù)以外的其他選擇(如 Template Method 設(shè)計模式的 non-virtual interface(NVI)手法,將 virtual 函數(shù)替換為 “函數(shù)指針成員變量”,以 tr1::function 成員變量替換 virtual 函數(shù),將繼承體系內(nèi)的 virtual 函數(shù)替換為另一個繼承體系內(nèi)的 virtual 函數(shù))

  36. 絕不重新定義繼承而來的 non-virtual 函數(shù)

  37. 絕不重新定義繼承而來的缺省參數(shù)值,因為缺省參數(shù)值是靜態(tài)綁定(statically bound),而 virtual 函數(shù)卻是動態(tài)綁定(dynamically bound)

  38. 通過復(fù)合塑模 has-a(有一個)或 “根據(jù)某物實現(xiàn)出”(在應(yīng)用域(application domain),復(fù)合意味 has-a(有一個);在實現(xiàn)域(implementation domain),復(fù)合意味著 is-implemented-in-terms-of(根據(jù)某物實現(xiàn)出))

  39. 明智而審慎地使用 private 繼承(private 繼承意味著 is-implemented-in-terms-of(根據(jù)某物實現(xiàn)出),盡可能使用復(fù)合,當(dāng) derived class 需要訪問 protected base class 的成員,或需要重新定義繼承而來的時候 virtual 函數(shù),或需要 empty base 最優(yōu)化時,才使用 private 繼承)

  40. 明智而審慎地使用多重繼承(多繼承比單一繼承復(fù)雜,可能導(dǎo)致新的歧義性,以及對 virtual 繼承的需要,但確有正當(dāng)用途,如 “public 繼承某個 interface class” 和 “private 繼承某個協(xié)助實現(xiàn)的 class”;virtual 繼承可解決多繼承下菱形繼承的二義性問題,但會增加大小、速度、初始化及賦值的復(fù)雜度等等成本)

  41. 了解隱式接口和編譯期多態(tài)(class 和 templates 都支持接口(interfaces)和多態(tài)(polymorphism);class 的接口是以簽名為中心的顯式的(explicit),多態(tài)則是通過 virtual 函數(shù)發(fā)生于運行期;template 的接口是奠基于有效表達(dá)式的隱式的(implicit),多態(tài)則是通過 template 具現(xiàn)化和函數(shù)重載解析(function overloading resolution)發(fā)生于編譯期)

  42. 了解 typename 的雙重意義(聲明 template 類型參數(shù)是,前綴關(guān)鍵字 class 和 typename 的意義完全相同;請使用關(guān)鍵字 typename 標(biāo)識嵌套從屬類型名稱,但不得在基類列(base class lists)或成員初值列(member initialization list)內(nèi)以它作為 basee class 修飾符)

  43. 學(xué)習(xí)處理模板化基類內(nèi)的名稱(可在 derived class templates 內(nèi)通過 this-> 指涉 base class templates 內(nèi)的成員名稱,或藉由一個明白寫出的 “base class 資格修飾符” 完成)

  44. 將與參數(shù)無關(guān)的代碼抽離 templates(因類型模板參數(shù)(non-type template parameters)而造成代碼膨脹往往可以通過函數(shù)參數(shù)或 class 成員變量替換 template 參數(shù)來消除;因類型參數(shù)(type parameters)而造成的代碼膨脹往往可以通過讓帶有完全相同二進制表述(binary representations)的實現(xiàn)類型(instantiation types)共享實現(xiàn)碼)

  45. 運用成員函數(shù)模板接受所有兼容類型(請使用成員函數(shù)模板(member function templates)生成 “可接受所有兼容類型” 的函數(shù);聲明 member templates 用于 “泛化 copy 構(gòu)造” 或 “泛化 assignment 操作” 時還需要聲明正常的 copy 構(gòu)造函數(shù)和 copy assignment 操作符)

  46. 需要類型轉(zhuǎn)換時請為模板定義非成員函數(shù)(當(dāng)我們編寫一個 class template,而它所提供之 “與此 template 相關(guān)的” 函數(shù)支持 “所有參數(shù)之隱式類型轉(zhuǎn)換” 時,請將那些函數(shù)定義為 “class template 內(nèi)部的 friend 函數(shù)”)

  47. 請使用 traits classes 表現(xiàn)類型信息(traits classes 通過 templates 和 “templates 特化” 使得 “類型相關(guān)信息” 在編譯期可用,通過重載技術(shù)(overloading)實現(xiàn)在編譯期對類型執(zhí)行 if…else 測試)

  48. 認(rèn)識 template 元編程(模板元編程(TMP,template metaprogramming)可將工作由運行期移往編譯期,因此得以實現(xiàn)早期錯誤偵測和更高的執(zhí)行效率;TMP 可被用來生成 “給予政策選擇組合”(based on combinations of policy choices)的客戶定制代碼,也可用來避免生成對某些特殊類型并不適合的代碼)

  49. 了解 new-handler 的行為(set_new_handler 允許客戶指定一個在內(nèi)存分配無法獲得滿足時被調(diào)用的函數(shù);nothrow new 是一個頗具局限的工具,因為它只適用于內(nèi)存分配(operator new),后繼的構(gòu)造函數(shù)調(diào)用還是可能拋出異常)

Google C++ Style Guide

英文:Google C++ Style Guide  :http:///RqhluJP

中文:C++ 風(fēng)格指南:http:///ELDTnur

Google C++ Style Guide 圖

圖片來源于:CSDN . 一張圖總結(jié)Google C++編程規(guī)范(Google C++ Style Guide)

STL

STL 索引

STL 方法含義索引:http:///E4WMXXs

STL 容器

容器的詳細(xì)說明:http:///E4WMXXs

容器底層數(shù)據(jù)結(jié)構(gòu)時間復(fù)雜度有無序可不可重復(fù)其他
array數(shù)組隨機讀改 O(1)無序可重復(fù)支持快速隨機訪問
vector數(shù)組隨機讀改、尾部插入、尾部刪除 O(1)
頭部插入、頭部刪除 O(n)
無序可重復(fù)支持快速隨機訪問
list雙向鏈表插入、刪除 O(1)
隨機讀改 O(n)
無序可重復(fù)支持快速增刪
deque雙端隊列頭尾插入、頭尾刪除 O(1)無序可重復(fù)一個中央控制器 + 多個緩沖區(qū),支持首尾快速增刪,支持隨機訪問
stackdeque / list頂部插入、頂部刪除 O(1)無序可重復(fù)deque 或 list 封閉頭端開口,不用 vector 的原因應(yīng)該是容量大小有限制,擴容耗時
queuedeque / list尾部插入、頭部刪除 O(1)無序可重復(fù)deque 或 list 封閉頭端開口,不用 vector 的原因應(yīng)該是容量大小有限制,擴容耗時
priority_queuevector + max-heap插入、刪除 O(log2n)有序可重復(fù)vector容器+heap處理規(guī)則
set紅黑樹插入、刪除、查找 O(log2n)有序不可重復(fù)
multiset紅黑樹插入、刪除、查找 O(log2n)有序可重復(fù)
map紅黑樹插入、刪除、查找 O(log2n)有序不可重復(fù)
multimap紅黑樹插入、刪除、查找 O(log2n)有序可重復(fù)
hash_set哈希表插入、刪除、查找 O(1) 最差 O(n)無序不可重復(fù)
hash_multiset哈希表插入、刪除、查找 O(1) 最差 O(n)無序可重復(fù)
hash_map哈希表插入、刪除、查找 O(1) 最差 O(n)無序不可重復(fù)
hash_multimap哈希表插入、刪除、查找 O(1) 最差 O(n)無序可重復(fù)

STL 算法

http:///aEv0DV

算法底層算法時間復(fù)雜度可不可重復(fù)
find順序查找O(n)可重復(fù)
sort內(nèi)省排序O(n*log2n)可重復(fù)

數(shù)據(jù)結(jié)構(gòu)

順序結(jié)構(gòu)

順序棧(Sequence Stack)

SqStack.cpp:http:///E4WxO0b

順序棧數(shù)據(jù)結(jié)構(gòu)和圖片

typedef struct {
    ElemType *elem;
    int top;
    int size;
    int increment;
} SqSrack;

隊列(Sequence Queue)

隊列數(shù)據(jù)結(jié)構(gòu)

typedef struct {
    ElemType * elem;
    int front;
    int rear;
    int maxSize;
}SqQueue;
非循環(huán)隊列

非循環(huán)隊列圖片

SqQueue.rear++

循環(huán)隊列

循環(huán)隊列圖片

SqQueue.rear = (SqQueue.rear + 1) % SqQueue.maxSize

順序表(Sequence List)

SqList.cpp

順序表數(shù)據(jù)結(jié)構(gòu)和圖片

typedef struct {
    ElemType *elem;
    int length;
    int size;
    int increment;
} SqList;

鏈?zhǔn)浇Y(jié)構(gòu)

LinkList.cpp

LinkList_with_head.cpp

鏈?zhǔn)綌?shù)據(jù)結(jié)構(gòu)

typedef struct LNode {
    ElemType data;
    struct LNode *next;
} LNode, *LinkList; 

鏈隊列(Link Queue)

鏈隊列圖片

線性表的鏈?zhǔn)奖硎?/span>

單鏈表(Link List)

單鏈表圖片

雙向鏈表(Du-Link-List)

雙向鏈表圖片

循環(huán)鏈表(Cir-Link-List)

循環(huán)鏈表圖片

哈希表

HashTable.cpp

概念

哈希函數(shù):H(key): K -> D , key ∈ K

構(gòu)造方法

  • 直接定址法

  • 除留余數(shù)法

  • 數(shù)字分析法

  • 折疊法

  • 平方取中法

沖突處理方法

  • 鏈地址法:key 相同的用單鏈表鏈接

  • 開放定址法

    • 線性探測法:key 相同 -> 放到 key 的下一個位置,`Hi = (H(key) + i) % m`

    • 二次探測法:key 相同 -> 放到 `Di = 1^2, -1^2, …, ±(k)^2,(k<=m/2)`

    • 隨機探測法:`H = (H(key) + 偽隨機數(shù)) % m`

線性探測的哈希表數(shù)據(jù)結(jié)構(gòu)

線性探測的哈希表數(shù)據(jù)結(jié)構(gòu)和圖片

typedef char KeyType;

typedef struct {
    KeyType key;
}RcdType;

typedef struct {
    RcdType *rcd;
    int size;
    int count;
    bool *tag;
}HashTable;

遞歸

概念

函數(shù)直接或間接地調(diào)用自身

遞歸與分治

  • 分治法

    • 問題的分解

    • 問題規(guī)模的分解

  • 折半查找(遞歸)

  • 歸并查找(遞歸)

  • 快速排序(遞歸)

遞歸與迭代

  • 迭代:反復(fù)利用變量舊值推出新值

  • 折半查找(迭代)

  • 歸并查找(迭代)

廣義表

頭尾鏈表存儲表示

廣義表的頭尾鏈表存儲表示和圖片

// 廣義表的頭尾鏈表存儲表示
typedef enum {ATOM, LIST} ElemTag;
// ATOM==0:原子,LIST==1:子表
typedef struct GLNode {
    ElemTag tag;
    // 公共部分,用于區(qū)分原子結(jié)點和表結(jié)點
    union {
        // 原子結(jié)點和表結(jié)點的聯(lián)合部分
        AtomType atom;
        // atom 是原子結(jié)點的值域,AtomType 由用戶定義
        struct {
            struct GLNode *hp, *tp;
        } ptr;
        // ptr 是表結(jié)點的指針域,prt.hp 和 ptr.tp 分別指向表頭和表尾
    } a;
} *GList, GLNode;
擴展線性鏈表存儲表示

擴展線性鏈表存儲表示和圖片

// 廣義表的擴展線性鏈表存儲表示
typedef enum {ATOM, LIST} ElemTag;
// ATOM==0:原子,LIST==1:子表
typedef struct GLNode1 {
    ElemTag tag;
    // 公共部分,用于區(qū)分原子結(jié)點和表結(jié)點
    union {
        // 原子結(jié)點和表結(jié)點的聯(lián)合部分
        AtomType atom; // 原子結(jié)點的值域
        struct GLNode1 *hp; // 表結(jié)點的表頭指針
    } a;
    struct GLNode1 *tp;
    // 相當(dāng)于線性鏈表的 next,指向下一個元素結(jié)點
} *GList1, GLNode1;

二叉樹

BinaryTree.cpp

性質(zhì)

  1. 非空二叉樹第 i 層最多 2(i-1) 個結(jié)點 (i >= 1)

  2. 深度為 k 的二叉樹最多 2k - 1 個結(jié)點 (k >= 1)

  3. 度為 0 的結(jié)點數(shù)為 n0,度為 2 的結(jié)點數(shù)為 n2,則 n0 = n2 + 1

  4. 有 n 個結(jié)點的完全二叉樹深度 k = ? log2(n) ? + 1

  5. 對于含 n 個結(jié)點的完全二叉樹中編號為 i (1 <= i <= n) 的結(jié)點

    1. 若 i = 1,為根,否則雙親為 ? i / 2 ?

    2. 若 2i > n,則 i 結(jié)點沒有左孩子,否則孩子編號為 2i

    3. 若 2i + 1 > n,則 i 結(jié)點沒有右孩子,否則孩子編號為 2i + 1

存儲結(jié)構(gòu)

二叉樹數(shù)據(jù)結(jié)構(gòu)

typedef struct BiTNode
{

    TElemType data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
順序存儲

二叉樹順序存儲圖片

鏈?zhǔn)酱鎯?/span>

二叉樹鏈?zhǔn)酱鎯D片

遍歷方式

  • 先序遍歷

  • 中序遍歷

  • 后續(xù)遍歷

  • 層次遍歷

分類

  • 滿二叉樹

  • 完全二叉樹(堆)

    • 大頂堆:根 >= 左 && 根 >= 右

    • 小頂堆:根 <= 左 && 根 <= 右

  • 二叉查找樹(二叉排序樹):左 < 根 < 右

  • 平衡二叉樹(AVL樹):| 左子樹樹高 - 右子樹樹高 | <= 1

  • 最小失衡樹:平衡二叉樹插入新結(jié)點導(dǎo)致失衡的子樹:調(diào)整:

    • LL型:根的左孩子右旋

    • RR型:根的右孩子左旋

    • LR型:根的左孩子左旋,再右旋

    • RL型:右孩子的左子樹,先右旋,再左旋

其他樹及森林

樹的存儲結(jié)構(gòu)

  • 雙親表示法

  • 雙親孩子表示法

  • 孩子兄弟表示法

并查集

一種不相交的子集所構(gòu)成的集合 S = {S1, S2, …, Sn}

平衡二叉樹(AVL樹)

性質(zhì)
  • | 左子樹樹高 - 右子樹樹高 | <= 1

  • 平衡二叉樹必定是二叉搜索樹,反之則不一定

  • 最小二叉平衡樹的節(jié)點的公式:F(n)=F(n-1)+F(n-2)+1 (1 是根節(jié)點,F(xiàn)(n-1) 是左子樹的節(jié)點數(shù)量,F(xiàn)(n-2) 是右子樹的節(jié)點數(shù)量)

平衡二叉樹圖片

最小失衡樹

平衡二叉樹插入新結(jié)點導(dǎo)致失衡的子樹

調(diào)整:

  • LL 型:根的左孩子右旋

  • RR 型:根的右孩子左旋

  • LR 型:根的左孩子左旋,再右旋

  • RL 型:右孩子的左子樹,先右旋,再左旋

紅黑樹

紅黑樹的特征是什么?
  1. 節(jié)點是紅色或黑色。

  2. 根是黑色。

  3. 所有葉子都是黑色(葉子是 NIL 節(jié)點)。

  4. 每個紅色節(jié)點必須有兩個黑色的子節(jié)點。(從每個葉子到根的所有路徑上不能有兩個連續(xù)的紅色節(jié)點。)(新增節(jié)點的父節(jié)點必須相同)

  5. 從任一節(jié)點到其每個葉子的所有簡單路徑都包含相同數(shù)目的黑色節(jié)點。(新增節(jié)點必須為紅)

調(diào)整
  1. 變色

  2. 左旋

  3. 右旋

應(yīng)用
  • 關(guān)聯(lián)數(shù)組:如 STL 中的 map、set

紅黑樹、B 樹、B+ 樹的區(qū)別?
  • 紅黑樹的深度比較大,而 B 樹和 B+ 樹的深度則相對要小一些

  • B+ 樹則將數(shù)據(jù)都保存在葉子節(jié)點,同時通過鏈表的形式將他們連接在一起。

B 樹(B-tree)、B+ 樹(B+-tree)

B 樹、B+ 樹圖片

B 樹(B-tree)、B+ 樹(B+-tree)
特點
  • 一般化的二叉查找樹(binary search tree)

  • “矮胖”,內(nèi)部(非葉子)節(jié)點可以擁有可變數(shù)量的子節(jié)點(數(shù)量范圍預(yù)先定義好)

應(yīng)用
  • 大部分文件系統(tǒng)、數(shù)據(jù)庫系統(tǒng)都采用B樹、B+樹作為索引結(jié)構(gòu)

區(qū)別
  • B+樹中只有葉子節(jié)點會帶有指向記錄的指針(ROWID),而B樹則所有節(jié)點都帶有,在內(nèi)部節(jié)點出現(xiàn)的索引項不會再出現(xiàn)在葉子節(jié)點中。

  • B+樹中所有葉子節(jié)點都是通過指針連接在一起,而B樹不會。

B樹的優(yōu)點

對于在內(nèi)部節(jié)點的數(shù)據(jù),可直接得到,不必根據(jù)葉子節(jié)點來定位。

B+樹的優(yōu)點
  • 非葉子節(jié)點不會帶上 ROWID,這樣,一個塊中可以容納更多的索引項,一是可以降低樹的高度。二是一個內(nèi)部節(jié)點可以定位更多的葉子節(jié)點。

  • 葉子節(jié)點之間通過指針來連接,范圍掃描將十分簡單,而對于B樹來說,則需要在葉子節(jié)點和內(nèi)部節(jié)點不停的往返移動。

B 樹、B+ 樹區(qū)別來自:differences-between-b-trees-and-b-trees、B樹和B+樹的區(qū)別:

http:///RrBAaZa

http:///E4WJhmZ

八叉樹

八叉樹圖片

八叉樹(octree),或稱八元樹,是一種用于描述三維空間(劃分空間)的樹狀數(shù)據(jù)結(jié)構(gòu)。八叉樹的每個節(jié)點表示一個正方體的體積元素,每個節(jié)點有八個子節(jié)點,這八個子節(jié)點所表示的體積元素加在一起就等于父節(jié)點的體積。一般中心點作為節(jié)點的分叉中心。

用途
  • 三維計算機圖形

  • 最鄰近搜索

算法

排序

http:///E4WJUGz

排序算法平均時間復(fù)雜度最差時間復(fù)雜度空間復(fù)雜度數(shù)據(jù)對象穩(wěn)定性
冒泡排序O(n2)O(n2)O(1)穩(wěn)定
選擇排序O(n2)O(n2)O(1)數(shù)組不穩(wěn)定、鏈表穩(wěn)定
插入排序O(n2)O(n2)O(1)穩(wěn)定
快速排序O(n*log2n)O(n2)O(log2n)不穩(wěn)定
堆排序O(n*log2n)O(n*log2n)O(1)不穩(wěn)定
歸并排序O(n*log2n)O(n*log2n)O(n)穩(wěn)定
希爾排序O(n*log2n)O(n2)O(1)不穩(wěn)定
計數(shù)排序O(n+m)O(n+m)O(n+m)穩(wěn)定
桶排序O(n)O(n)O(m)穩(wěn)定
基數(shù)排序O(k*n)O(n2)
穩(wěn)定

均按從小到大排列

  • k:代表數(shù)值中的 “數(shù)位” 個數(shù)

  • n:代表數(shù)據(jù)規(guī)模

  • m:代表數(shù)據(jù)的最大值減最小值

  • 來自:wikipedia . 排序算法

查找

查找算法平均時間復(fù)雜度空間復(fù)雜度查找條件
順序查找O(n)O(1)無序或有序
二分查找(折半查找)O(log2n)O(1)有序
插值查找O(log2(log2n))O(1)有序
斐波那契查找O(log2n)O(1)有序
哈希查找O(1)O(n)無序或有序
二叉查找樹(二叉搜索樹查找)O(log2n)

紅黑樹O(log2n)

2-3樹O(log2n - log3n)

B樹/B+樹O(log2n)

圖搜索算法

圖搜索算法數(shù)據(jù)結(jié)構(gòu)遍歷時間復(fù)雜度空間復(fù)雜度
BFS廣度優(yōu)先搜索鄰接矩陣
鄰接鏈表
O(|v|2)
O(|v|+|E|)
O(|v|2)
O(|v|+|E|)
DFS深度優(yōu)先搜索鄰接矩陣
鄰接鏈表
O(|v|2)
O(|v|+|E|)
O(|v|2)
O(|v|+|E|)

其他算法

算法思想應(yīng)用
分治法把一個復(fù)雜的問題分成兩個或更多的相同或相似的子問題,直到最后子問題可以簡單的直接求解,原問題的解即子問題的解的合并循環(huán)賽日程安排問題、排序算法(快速排序、歸并排序)
動態(tài)規(guī)劃通過把原問題分解為相對簡單的子問題的方式求解復(fù)雜問題的方法,適用于有重疊子問題和最優(yōu)子結(jié)構(gòu)性質(zhì)的問題背包問題、斐波那契數(shù)列
貪心法一種在每一步選擇中都采取在當(dāng)前狀態(tài)下最好或最優(yōu)(即最有利)的選擇,從而希望導(dǎo)致結(jié)果是最好或最優(yōu)的算法旅行推銷員問題(最短路徑問題)、最小生成樹、哈夫曼編碼

Problems

Single Problem

  • Chessboard Coverage Problem(棋盤覆蓋問題)

  • Knapsack Problem(背包問題)

  • Neumann Neighbor Problem(馮諾依曼鄰居問題)

  • Round Robin Problem(循環(huán)賽日程安排問題)

  • Tubing Problem(輸油管道問題)

Leetcode Problems

  • Github . haoel/leetcode

  • Github . pezy/LeetCode

劍指 Offer

  • Github . zhedahht/CodingInterviewChinese2

  • Github . gatieme/CodingInterviews

Cracking the Coding Interview 程序員面試金典

  • Github . careercup/ctci

  • ??途W(wǎng) . 程序員面試金典

??途W(wǎng)

  • ??途W(wǎng) . 在線編程專題

操作系統(tǒng)

進程與線程

對于有線程系統(tǒng):

  • 進程是資源分配的獨立單位

  • 線程是資源調(diào)度的獨立單位

對于無線程系統(tǒng):

  • 進程是資源調(diào)度、分配的獨立單位

進程之間的通信方式以及優(yōu)缺點

  • 管道(PIPE)

    • 優(yōu)點:簡單方便

    • 缺點:

    • 優(yōu)點:可以實現(xiàn)任意關(guān)系的進程間的通信

    • 缺點:

    • 有名管道:一種半雙工的通信方式,它允許無親緣關(guān)系進程間的通信

    • 無名管道:一種半雙工的通信方式,只能在具有親緣關(guān)系的進程間使用(父子進程)

    1. 局限于單向通信

    2. 只能創(chuàng)建在它的進程以及其有親緣關(guān)系的進程之間

    3. 緩沖區(qū)有限

    1. 長期存于系統(tǒng)中,使用不當(dāng)容易出錯

    2. 緩沖區(qū)有限

  • 信號量(Semaphore):一個計數(shù)器,可以用來控制多個線程對共享資源的訪問

    • 優(yōu)點:可以同步進程

    • 缺點:信號量有限

  • 信號(Signal):一種比較復(fù)雜的通信方式,用于通知接收進程某個事件已經(jīng)發(fā)生

  • 消息隊列(Message Queue):是消息的鏈表,存放在內(nèi)核中并由消息隊列標(biāo)識符標(biāo)識

    • 優(yōu)點:可以實現(xiàn)任意進程間的通信,并通過系統(tǒng)調(diào)用函數(shù)來實現(xiàn)消息發(fā)送和接收之間的同步,無需考慮同步問題,方便

    • 缺點:信息的復(fù)制需要額外消耗 CPU 的時間,不適宜于信息量大或操作頻繁的場合

  • 共享內(nèi)存(Shared Memory):映射一段能被其他進程所訪問的內(nèi)存,這段共享內(nèi)存由一個進程創(chuàng)建,但多個進程都可以訪問

    • 優(yōu)點:無須復(fù)制,快捷,信息量大

    • 缺點:

    1. 通信是通過將共享空間緩沖區(qū)直接附加到進程的虛擬地址空間中來實現(xiàn)的,因此進程間的讀寫操作的同步問題

    2. 利用內(nèi)存緩沖區(qū)直接交換信息,內(nèi)存的實體存在于計算機中,只能同一個計算機系統(tǒng)中的諸多進程共享,不方便網(wǎng)絡(luò)通信

  • 套接字(Socket):可用于不同及其間的進程通信

    • 優(yōu)點:

    • 缺點:需對傳輸?shù)臄?shù)據(jù)進行解析,轉(zhuǎn)化成應(yīng)用級的數(shù)據(jù)。

    1. 傳輸數(shù)據(jù)為字節(jié)級,傳輸數(shù)據(jù)可自定義,數(shù)據(jù)量小效率高

    2. 傳輸數(shù)據(jù)時間短,性能高

    3. 適合于客戶端和服務(wù)器端之間信息實時交互

    4. 可以加密,數(shù)據(jù)安全性強

線程之間的通信方式

  • 鎖機制:包括互斥鎖/量(mutex)、讀寫鎖(reader-writer lock)、自旋鎖(spin lock)、條件變量(condition)

    • 互斥鎖/量(mutex):提供了以排他方式防止數(shù)據(jù)結(jié)構(gòu)被并發(fā)修改的方法。

    • 讀寫鎖(reader-writer lock):允許多個線程同時讀共享數(shù)據(jù),而對寫操作是互斥的。

    • 自旋鎖(spin lock)與互斥鎖類似,都是為了保護共享資源?;コ怄i是當(dāng)資源被占用,申請者進入睡眠狀態(tài);而自旋鎖則循環(huán)檢測保持著是否已經(jīng)釋放鎖。

    • 條件變量(condition):可以以原子的方式阻塞進程,直到某個特定條件為真為止。對條件的測試是在互斥鎖的保護下進行的。條件變量始終與互斥鎖一起使用。

  • 信號量機制(Semaphore)

    • 無名線程信號量

    • 命名線程信號量

  • 信號機制(Signal):類似進程間的信號處理

  • 屏障(barrier):屏障允許每個線程等待,直到所有的合作線程都達(dá)到某一點,然后從該點繼續(xù)執(zhí)行。

線程間的通信目的主要是用于線程同步,所以線程沒有像進程通信中的用于數(shù)據(jù)交換的通信機制

進程之間的通信方式以及優(yōu)缺點來源于:進程線程面試題總結(jié)

進程之間私有和共享的資源

  • 私有:地址空間、堆、全局變量、棧、寄存器

  • 共享:代碼段,公共數(shù)據(jù),進程目錄,進程 ID

線程之間私有和共享的資源

  • 私有:線程棧,寄存器,程序寄存器

  • 共享:堆,地址空間,全局變量,靜態(tài)變量

多進程與多線程間的對比、優(yōu)劣與選擇

對比
對比維度多進程多線程總結(jié)
數(shù)據(jù)共享、同步數(shù)據(jù)共享復(fù)雜,需要用 IPC;數(shù)據(jù)是分開的,同步簡單因為共享進程數(shù)據(jù),數(shù)據(jù)共享簡單,但也是因為這個原因?qū)е峦綇?fù)雜各有優(yōu)勢
內(nèi)存、CPU占用內(nèi)存多,切換復(fù)雜,CPU 利用率低占用內(nèi)存少,切換簡單,CPU 利用率高線程占優(yōu)
創(chuàng)建銷毀、切換創(chuàng)建銷毀、切換復(fù)雜,速度慢創(chuàng)建銷毀、切換簡單,速度很快線程占優(yōu)
編程、調(diào)試編程簡單,調(diào)試簡單編程復(fù)雜,調(diào)試復(fù)雜進程占優(yōu)
可靠性進程間不會互相影響一個線程掛掉將導(dǎo)致整個進程掛掉進程占優(yōu)
分布式適應(yīng)于多核、多機分布式;如果一臺機器不夠,擴展到多臺機器比較簡單適應(yīng)于多核分布式進程占優(yōu)
優(yōu)劣
優(yōu)劣多進程多線程
優(yōu)點編程、調(diào)試簡單,可靠性較高創(chuàng)建、銷毀、切換速度快,內(nèi)存、資源占用小
缺點創(chuàng)建、銷毀、切換速度慢,內(nèi)存、資源占用大編程、調(diào)試復(fù)雜,可靠性較差
選擇
  • 需要頻繁創(chuàng)建銷毀的優(yōu)先用線程

  • 需要進行大量計算的優(yōu)先使用線程

  • 強相關(guān)的處理用線程,弱相關(guān)的處理用進程

  • 可能要擴展到多機分布的用進程,多核分布的用線程

  • 都滿足需求的情況下,用你最熟悉、最拿手的方式

多進程與多線程間的對比、優(yōu)劣與選擇來自:多線程還是多進程的選擇及區(qū)別

Linux 內(nèi)核的同步方式

原因

在現(xiàn)代操作系統(tǒng)里,同一時間可能有多個內(nèi)核執(zhí)行流在執(zhí)行,因此內(nèi)核其實象多進程多線程編程一樣也需要一些同步機制來同步各執(zhí)行單元對共享數(shù)據(jù)的訪問。尤其是在多處理器系統(tǒng)上,更需要一些同步機制來同步不同處理器上的執(zhí)行單元對共享的數(shù)據(jù)的訪問。

同步方式

  • 原子操作

  • 信號量(semaphore)

  • 讀寫信號量(rw_semaphore)

  • 自旋鎖(spinlock)

  • 大內(nèi)核鎖(BKL,Big Kernel Lock)

  • 讀寫鎖(rwlock)

  • 大讀者鎖(brlock-Big Reader Lock)

  • 讀-拷貝修改(RCU,Read-Copy Update)

  • 順序鎖(seqlock)

來自:Linux 內(nèi)核的同步機制,第 1 部分、Linux 內(nèi)核的同步機制,第 2 部分

死鎖

原因

  • 系統(tǒng)資源不足

  • 資源分配不當(dāng)

  • 進程運行推進順序不合適

產(chǎn)生條件

  • 互斥

  • 請求和保持

  • 不剝奪

  • 環(huán)路

預(yù)防

  • 打破互斥條件:改造獨占性資源為虛擬資源,大部分資源已無法改造。

  • 打破不可搶占條件:當(dāng)一進程占有一獨占性資源后又申請一獨占性資源而無法滿足,則退出原占有的資源。

  • 打破占有且申請條件:采用資源預(yù)先分配策略,即進程運行前申請全部資源,滿足則運行,不然就等待,這樣就不會占有且申請。

  • 打破循環(huán)等待條件:實現(xiàn)資源有序分配策略,對所有設(shè)備實現(xiàn)分類編號,所有進程只能采用按序號遞增的形式申請資源。

  • 有序資源分配法

  • 銀行家算法

文件系統(tǒng)

  • Windows:FCB 表 + FAT + 位圖

  • Unix:inode + 混合索引 + 成組鏈接

主機字節(jié)序與網(wǎng)絡(luò)字節(jié)序

主機字節(jié)序(CPU 字節(jié)序)

概念

主機字節(jié)序又叫 CPU 字節(jié)序,其不是由操作系統(tǒng)決定的,而是由 CPU 指令集架構(gòu)決定的。主機字節(jié)序分為兩種:

  • 大端字節(jié)序(Big Endian):高序字節(jié)存儲在低位地址,低序字節(jié)存儲在高位地址

  • 小端字節(jié)序(Little Endian):高序字節(jié)存儲在高位地址,低序字節(jié)存儲在低位地址

存儲方式

32 位整數(shù) 0x12345678 是從起始位置為 0x00 的地址開始存放,則:

內(nèi)存地址0x000x010x020x03
大端12345678
小端78563412

大端小端圖片

大端序

小端序
判斷大端小端

判斷大端小端

可以這樣判斷自己 CPU 字節(jié)序是大端還是小端:

#include <iostream>
using namespace std;

int main()
{
    int i = 0x12345678;

    if (*((char*)&i) == 0x12)
        cout << '大端' << endl;
    else    
        cout << '小端' << endl;

    return 0;
}
各架構(gòu)處理器的字節(jié)序
  • x86(Intel、AMD)、MOS Technology 6502、Z80、VAX、PDP-11 等處理器為小端序;

  • Motorola 6800、Motorola 68000、PowerPC 970、System/370、SPARC(除 V9 外)等處理器為大端序;

  • ARM(默認(rèn)小端序)、PowerPC(除 PowerPC 970 外)、DEC Alpha、SPARC V9、MIPS、PA-RISC 及 IA64 的字節(jié)序是可配置的。

網(wǎng)絡(luò)字節(jié)序

網(wǎng)絡(luò)字節(jié)順序是 TCP/IP 中規(guī)定好的一種數(shù)據(jù)表示格式,它與具體的 CPU 類型、操作系統(tǒng)等無關(guān),從而可以保重數(shù)據(jù)在不同主機之間傳輸時能夠被正確解釋。

網(wǎng)絡(luò)字節(jié)順序采用:大端(Big Endian)排列方式。

頁面置換算法

在地址映射過程中,若在頁面中發(fā)現(xiàn)所要訪問的頁面不在內(nèi)存中,則產(chǎn)生缺頁中斷。當(dāng)發(fā)生缺頁中斷時,如果操作系統(tǒng)內(nèi)存中沒有空閑頁面,則操作系統(tǒng)必須在內(nèi)存選擇一個頁面將其移出內(nèi)存,以便為即將調(diào)入的頁面讓出空間。而用來選擇淘汰哪一頁的規(guī)則叫做頁面置換算法。

分類

  • 全局置換:在整個內(nèi)存空間置換

  • 局部置換:在本進程中進行置換

算法

全局:

  • 工作集算法

  • 缺頁率置換算法

局部:

  • 最佳置換算法(OPT)

  • 先進先出置換算法(FIFO)

  • 最近最久未使用(LRU)算法

  • 時鐘(Clock)置換算法

計算機網(wǎng)絡(luò)

計算機經(jīng)網(wǎng)絡(luò)體系結(jié)構(gòu):

計算機經(jīng)網(wǎng)絡(luò)體系結(jié)構(gòu)

各層作用及協(xié)議

分層作用協(xié)議
物理層通過媒介傳輸比特,確定機械及電氣規(guī)范(比特 Bit)RJ45、CLOCK、IEEE802.3(中繼器,集線器)
數(shù)據(jù)鏈路層將比特組裝成幀和點到點的傳遞(幀 Frame)PPP、FR、HDLC、VLAN、MAC(網(wǎng)橋,交換機)
網(wǎng)絡(luò)層負(fù)責(zé)數(shù)據(jù)包從源到宿的傳遞和網(wǎng)際互連(包 Packet)IP、ICMP、ARP、RARP、OSPF、IPX、RIP、IGRP(路由器)
運輸層提供端到端的可靠報文傳遞和錯誤恢復(fù)( 段Segment)TCP、UDP、SPX
會話層建立、管理和終止會話(會話協(xié)議數(shù)據(jù)單元 SPDU)NFS、SQL、NETBIOS、RPC
表示層對數(shù)據(jù)進行翻譯、加密和壓縮(表示協(xié)議數(shù)據(jù)單元 PPDU)JPEG、MPEG、ASII
應(yīng)用層允許訪問OSI環(huán)境的手段(應(yīng)用協(xié)議數(shù)據(jù)單元 APDU)FTP、DNS、Telnet、SMTP、HTTP、WWW、NFS

物理層

  • 傳輸數(shù)據(jù)的單位 ———— 比特

  • 數(shù)據(jù)傳輸系統(tǒng):源系統(tǒng)(源點、發(fā)送器) --> 傳輸系統(tǒng) --> 目的系統(tǒng)(接收器、終點)

通道:

  • 單向通道(單工通道):只有一個方向通信,沒有反方向交互,如廣播

  • 雙向交替通行(半雙工通信):通信雙方都可發(fā)消息,但不能同時發(fā)送或接收

  • 雙向同時通信(全雙工通信):通信雙方可以同時發(fā)送和接收信息

通道復(fù)用技術(shù):

  • 頻分復(fù)用(FDM,F(xiàn)requency Division Multiplexing):不同用戶在不同頻帶,所用用戶在同樣時間占用不同帶寬資源

  • 時分復(fù)用(TDM,Time Division Multiplexing):不同用戶在同一時間段的不同時間片,所有用戶在不同時間占用同樣的頻帶寬度

  • 波分復(fù)用(WDM,Wavelength Division Multiplexing):光的頻分復(fù)用

  • 碼分復(fù)用(CDM,Code Division Multiplexing):不同用戶使用不同的碼,可以在同樣時間使用同樣頻帶通信

數(shù)據(jù)鏈路層

主要信道:

  • 點對點信道

  • 廣播信道

點對點信道

  • 數(shù)據(jù)單元 ———— 幀

三個基本問題:

  • 封裝成幀:把網(wǎng)絡(luò)層的 IP 數(shù)據(jù)報封裝成幀,SOH - 數(shù)據(jù)部分 - EOT

  • 透明傳輸:不管數(shù)據(jù)部分什么字符,都能傳輸出去;可以通過字節(jié)填充方法解決(沖突字符前加轉(zhuǎn)義字符)

  • 差錯檢測:降低誤碼率(BER,Bit Error Rate),廣泛使用循環(huán)冗余檢測(CRC,Cyclic Redundancy Check)

點對點協(xié)議(Point-to-Point Protocol):

  • 點對點協(xié)議(Point-to-Point Protocol):用戶計算機和 ISP 通信時所使用的協(xié)議

廣播信道

廣播通信:

  • 硬件地址(物理地址、MAC 地址)

  • 單播(unicast)幀(一對一):收到的幀的 MAC 地址與本站的硬件地址相同

  • 廣播(broadcast)幀(一對全體):發(fā)送給本局域網(wǎng)上所有站點的幀

  • 多播(multicast)幀(一對多):發(fā)送給本局域網(wǎng)上一部分站點的幀

來源:https://github.com/huihut/interview

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多