C++那些問(wèn)題

參考鏈接:

-在設(shè)計(jì)類的時(shí)候瘩例,一個(gè)原則就是對(duì)于不改變數(shù)據(jù)成員的成員函數(shù)都要在后面加 const蜓斧,而對(duì)于改變數(shù)據(jù)成員的成員函數(shù)不能加 const达布。所以 const 關(guān)鍵字對(duì)成員函數(shù)的行為作了更加明確的限定:有 const 修飾的成員函數(shù)(指 const 放在函數(shù)參數(shù)表的后面骇窍,而不是在函數(shù)前面或者參數(shù)表內(nèi))镐确,只能讀取數(shù)據(jù)成員,不能改變數(shù)據(jù)成員饼煞;沒(méi)有 const 修飾的成員函數(shù)源葫,對(duì)數(shù)據(jù)成員則是可讀可寫(xiě)的。

除此之外砖瞧,在類的成員函數(shù)后面加 const 還有什么好處呢息堂?
“獲得能力:可以操作常量對(duì)象”,其實(shí)應(yīng)該是常量(即 const)對(duì)象可以調(diào)用 const 成員函數(shù)块促,而不能調(diào)用非const修飾的函數(shù)荣堰。

對(duì)于const成員函數(shù),"不能修改類的數(shù)據(jù)成員竭翠,不能在函數(shù)中調(diào)用其他不是const的函數(shù)"振坚,這是由const的屬性決定的 。

  • static靜態(tài)成員變量不能在類的內(nèi)部初始化斋扰。在類的內(nèi)部只是聲明渡八,定義必須在類定義體的外部,通常在類的實(shí)現(xiàn)文件中初始化传货。

  • 由于聲明為static的變量只被初始化一次屎鳍,因?yàn)樗鼈冊(cè)趩为?dú)的靜態(tài)存儲(chǔ)中分配了空間,因此類中的靜態(tài)變量由對(duì)象共享问裕。對(duì)于不同的對(duì)象逮壁,不能有相同靜態(tài)變量的多個(gè)副本。也是因?yàn)檫@個(gè)原因僻澎,靜態(tài)變量不能使用構(gòu)造函數(shù)初始化貌踏。

  • 類中的靜態(tài)變量應(yīng)由用戶使用類外的類名和范圍解析運(yùn)算符顯式初始化

  • 靜態(tài)對(duì)象的范圍是貫穿程序的生命周期

  • 一個(gè)對(duì)象的this指針并不是對(duì)象本身的一部分十饥,不會(huì)影響sizeof(對(duì)象)的結(jié)果窟勃。

  • 在類的非靜態(tài)成員函數(shù)中返回類對(duì)象本身的時(shí)候,直接使用 return *this逗堵。

  • 當(dāng)參數(shù)與成員變量名相同時(shí)秉氧,如this->n = n (不能寫(xiě)成n = n)。

  • this在成員函數(shù)的開(kāi)始執(zhí)行前構(gòu)造蜒秤,在成員的執(zhí)行結(jié)束后清除汁咏。

  • this類型為const A* const。A為類作媚。

  • 內(nèi)聯(lián)能提高函數(shù)效率恩急,但并不是所有的函數(shù)都定義成內(nèi)聯(lián)函數(shù)瞎领!內(nèi)聯(lián)是以代碼膨脹(復(fù)制)為代價(jià),僅僅省去了函數(shù)調(diào)用的開(kāi)銷,從而提高函數(shù)的執(zhí)行效率茸歧。

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

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

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

  • sizeof: 普通繼承伺通,派生類繼承了所有基類的函數(shù)與成員,要按照字節(jié)對(duì)齊來(lái)計(jì)算大小

  • sizeof:虛函數(shù)繼承逢享,不管是單繼承還是多繼承泵殴,都是繼承了基類的vptr。(32位操作系統(tǒng)4字節(jié)拼苍,64位操作系統(tǒng) 8字節(jié))笑诅!

  • sizeof: 靜態(tài)變量不影響類的大小

  • sizeof:對(duì)于包含虛函數(shù)的類,不管有多少個(gè)虛函數(shù)疮鲫,只有一個(gè)虛指針,vptr的大小吆你。

  • sizeof: 派生類虛繼承多個(gè)虛函數(shù),會(huì)繼承所有虛函數(shù)的vptr俊犯。

  • 抽象類中:在成員函數(shù)內(nèi)可以調(diào)用純虛函數(shù)妇多,在構(gòu)造函數(shù)/析構(gòu)函數(shù)內(nèi)部不能使用純虛函數(shù)。

  • 如果一個(gè)類從抽象類派生而來(lái)燕侠,它必須實(shí)現(xiàn)了基類中的所有純虛函數(shù)者祖,才能成為非抽象類。

  • 抽象類至少包含一個(gè)純虛函數(shù)

  • 不能創(chuàng)建抽象類的對(duì)象

  • 抽象類的指針和引用 指向 由抽象類派生出來(lái)的類的對(duì)象

  • 派生類沒(méi)有實(shí)現(xiàn)純虛函數(shù)绢彤,那么派生類也會(huì)變?yōu)槌橄箢惼呶剩荒軇?chuàng)建抽象類的對(duì)象

  • 虛函數(shù)的調(diào)用取決于指向或者引用的對(duì)象的類型,而不是指針或者引用自身的類型茫舶。

  • 默認(rèn)參數(shù)是靜態(tài)綁定的械巡,虛函數(shù)是動(dòng)態(tài)綁定的。 默認(rèn)參數(shù)的使用需要看指針或者引用本身的類型饶氏,而不是對(duì)象的類型讥耗。

  • 靜態(tài)函數(shù)不可以聲明為虛函數(shù),同時(shí)也不能被const 和 volatile關(guān)鍵字修飾

  • 為什么構(gòu)造函數(shù)不可以為虛函數(shù)疹启?
    解:盡管虛函數(shù)表vtable是在編譯階段就已經(jīng)建立的古程,但指向虛函數(shù)表的指針vptr是在運(yùn)行階段實(shí)例化對(duì)象時(shí)才產(chǎn)生的。 如果類含有虛函數(shù)喊崖,編譯器會(huì)在構(gòu)造函數(shù)中添加代碼來(lái)創(chuàng)建vptr挣磨。 問(wèn)題來(lái)了菲宴,如果構(gòu)造函數(shù)是虛的,那么它需要vptr來(lái)訪問(wèn)vtable趋急,可這個(gè)時(shí)候vptr還沒(méi)產(chǎn)生喝峦。 因此,構(gòu)造函數(shù)不可以為虛函數(shù)呜达。

  • 虛函數(shù)可以被私有化谣蠢,但有一些細(xì)節(jié)需要注意。
    1 基類指針指向繼承類對(duì)象查近,則調(diào)用繼承類對(duì)象的函數(shù)眉踱;
    2 int main()必須聲明為Base類的友元,否則編譯失敗霜威。 編譯器報(bào)錯(cuò): ptr無(wú)法訪問(wèn)私有函數(shù)谈喳。
    3 當(dāng)然,把基類聲明為public戈泼, 繼承類為private婿禽,該問(wèn)題就不存在了。

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

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

  • 指針可以是 volatile

  • 斷言挽绩,是宏膛壹,而非函數(shù)。assert 宏的原型定義在 (C)唉堪、(C++)中模聋,其作用是如果它的條件返回錯(cuò)誤,則終止程序執(zhí)行唠亚×捶剑可以通過(guò)定義 NDEBUG 來(lái)關(guān)閉 assert,但是需要在源代碼的開(kāi)頭趾撵,include 之前侄柔。

  • 斷言主要用于檢查邏輯上不可能的情況。

  • 它們可用于檢查代碼在開(kāi)始運(yùn)行之前所期望的狀態(tài)占调,或者在運(yùn)行完成后檢查狀態(tài)。與正常的錯(cuò)誤處理不同移剪,斷言通常在運(yùn)行時(shí)被禁用究珊。

  • 忽略斷言:在代碼開(kāi)頭加上:#define NDEBUG // 加上這行,則 assert 不可用

  • extern "C"全部都放在于cpp程序相關(guān)文件或其頭文件中纵苛。

  • 在C中struct只單純的用作數(shù)據(jù)的復(fù)合類型剿涮,也就是說(shuō)言津,在結(jié)構(gòu)體聲明中只能將數(shù)據(jù)成員放在里面,而不能將函數(shù)放在里面取试。

  • 在C結(jié)構(gòu)體聲明中不能使用C++訪問(wèn)修飾符悬槽,如:public、protected瞬浓、private 而在C++中可以使用初婆。

  • 在C中定義結(jié)構(gòu)體變量,如果使用了下面定義必須加struct猿棉。

  • C的結(jié)構(gòu)體不能繼承(沒(méi)有這一概念)磅叛。

  • 若結(jié)構(gòu)體的名字與函數(shù)名相同,可以正常運(yùn)行且正常的調(diào)用萨赁!例如:可以定義與 struct Base 不沖突的 void Base() {}弊琴。

  • 與C對(duì)比如下:
    1.C++結(jié)構(gòu)體中不僅可以定義數(shù)據(jù),還可以定義函數(shù)杖爽。
    2.C++結(jié)構(gòu)體中可以使用訪問(wèn)修飾符敲董,如:public、protected慰安、private 臣缀。
    3.C++結(jié)構(gòu)體使用可以直接使用不帶struct。
    4.C++繼承若結(jié)構(gòu)體的名字與函數(shù)名相同泻帮,可以正常運(yùn)行且正常的調(diào)用精置!但是定義結(jié)構(gòu)體變量時(shí)候只用帶struct的!

  • union
    1.默認(rèn)訪問(wèn)控制符為 public
    2.可以含有構(gòu)造函數(shù)锣杂、析構(gòu)函數(shù)
    3.不能含有引用類型的成員
    4.不能繼承自其他類脂倦,不能作為基類
    5.不能含有虛函數(shù)
    6.匿名 union 在定義所在作用域可直接訪問(wèn) union 成員
    7.匿名 union 不能包含 protected 成員或 private 成員
    8.全局匿名聯(lián)合必須是靜態(tài)(static)的

  • .C實(shí)現(xiàn)多態(tài)

  • 封裝
    C語(yǔ)言中是沒(méi)有class類這個(gè)概念的,但是有struct結(jié)構(gòu)體元莫,我們可以考慮使用struct來(lái)模擬赖阻;
    使用函數(shù)指針把屬性與方法封裝到結(jié)構(gòu)體中。
  • 繼承
    結(jié)構(gòu)體嵌套
  • 多態(tài)踱蠢、
    類與子類方法的函數(shù)指針不同

在C語(yǔ)言的結(jié)構(gòu)體內(nèi)部是沒(méi)有成員函數(shù)的火欧,如果實(shí)現(xiàn)這個(gè)父結(jié)構(gòu)體和子結(jié)構(gòu)體共有的函數(shù)呢?我們可以考慮使用函數(shù)指針來(lái)模擬茎截。但是這樣處理存在一個(gè)缺陷就是:父子各自的函數(shù)指針之間指向的不是類似C++中維護(hù)的虛函數(shù)表而是一塊物理內(nèi)存苇侵,如果模擬的函數(shù)過(guò)多的話就會(huì)不容易維護(hù)了。

模擬多態(tài)企锌,必須保持函數(shù)指針變量對(duì)齊(在內(nèi)容上完全一致榆浓,而且變量對(duì)齊上也完全一致)。否則父類指針指向子類對(duì)象撕攒,運(yùn)行崩潰陡鹃!

#include <stdio.h>

// 重定義一個(gè)函數(shù)指針類型
typedef void (*pf) ();

/**
   父類 
 */ 
typedef struct _A
{
    pf _f;
}A;


/**
   子類
 */
typedef struct _B
{ 
    A _b; // 在子類中定義一個(gè)基類的對(duì)象即可實(shí)現(xiàn)對(duì)父類的繼承烘浦。 
}B;

void FunA() 
{
    printf("%s\n","Base A::fun()");
}

void FunB() 
{
    printf("%s\n","Derived B::fun()");
}


int main() 
{
    A a;
    B b;

    a._f = FunA;
    b._b._f = FunB;

    A *pa = &a;
    pa->_f();
    pa = (A *)&b;   // 讓父類指針指向子類的對(duì)象,由于類型不匹配所以要進(jìn)行強(qiáng)轉(zhuǎn) 
    pa->_f();
    return 0;
}
  • explicit 修飾構(gòu)造函數(shù)時(shí),可以防止隱式轉(zhuǎn)換和復(fù)制初始化
  • explicit 修飾轉(zhuǎn)換函數(shù)時(shí)萍鲸,可以防止隱式轉(zhuǎn)換闷叉,但按語(yǔ)境轉(zhuǎn)換除外
#include <iostream>

using namespace std;

struct A
{
    A(int) { }
    operator bool() const { return true; }
};

struct B
{
    explicit B(int) {}
    explicit operator bool() const { return true; }
};

void doA(A a) {}

void doB(B b) {}

int main()
{
    A a1(1);        // OK:直接初始化
    A a2 = 1;        // OK:復(fù)制初始化
    A a3{ 1 };        // OK:直接列表初始化
    A a4 = { 1 };        // OK:復(fù)制列表初始化
    A a5 = (A)1;        // OK:允許 static_cast 的顯式轉(zhuǎn)換 
    doA(1);            // OK:允許從 int 到 A 的隱式轉(zhuǎn)換
    if (a1);        // OK:使用轉(zhuǎn)換函數(shù) A::operator bool() 的從 A 到 bool 的隱式轉(zhuǎn)換
    bool a6(a1);        // OK:使用轉(zhuǎn)換函數(shù) A::operator bool() 的從 A 到 bool 的隱式轉(zhuǎn)換
    bool a7 = a1;        // OK:使用轉(zhuǎn)換函數(shù) A::operator bool() 的從 A 到 bool 的隱式轉(zhuǎn)換
    bool a8 = static_cast<bool>(a1);  // OK :static_cast 進(jìn)行直接初始化

    B b1(1);        // OK:直接初始化
//    B b2 = 1;        // 錯(cuò)誤:被 explicit 修飾構(gòu)造函數(shù)的對(duì)象不可以復(fù)制初始化
    B b3{ 1 };        // OK:直接列表初始化
//    B b4 = { 1 };        // 錯(cuò)誤:被 explicit 修飾構(gòu)造函數(shù)的對(duì)象不可以復(fù)制列表初始化
    B b5 = (B)1;        // OK:允許 static_cast 的顯式轉(zhuǎn)換
//    doB(1);            // 錯(cuò)誤:被 explicit 修飾構(gòu)造函數(shù)的對(duì)象不可以從 int 到 B 的隱式轉(zhuǎn)換
    if (b1);        // OK:被 explicit 修飾轉(zhuǎn)換函數(shù) B::operator bool() 的對(duì)象可以從 B 到 bool 的按語(yǔ)境轉(zhuǎn)換
    bool b6(b1);        // OK:被 explicit 修飾轉(zhuǎn)換函數(shù) B::operator bool() 的對(duì)象可以從 B 到 bool 的按語(yǔ)境轉(zhuǎn)換
//    bool b7 = b1;        // 錯(cuò)誤:被 explicit 修飾轉(zhuǎn)換函數(shù) B::operator bool() 的對(duì)象不可以隱式轉(zhuǎn)換
    bool b8 = static_cast<bool>(b1);  // OK:static_cast 進(jìn)行直接初始化

    return 0;
}
  • 友元提供了一種 普通函數(shù)或者類成員函數(shù) 訪問(wèn)另一個(gè)類中的私有或保護(hù)成員 的機(jī)制。也就是說(shuō)有兩種形式的友元:

(1)友元函數(shù):普通函數(shù)對(duì)一個(gè)訪問(wèn)某個(gè)類中的私有或保護(hù)成員脊阴。

(2)友元類:類A中的成員函數(shù)訪問(wèn)類B中的私有或保護(hù)成員

優(yōu)點(diǎn):提高了程序的運(yùn)行效率握侧。

缺點(diǎn):破壞了類的封裝性和數(shù)據(jù)的透明性。

總結(jié): - 能訪問(wèn)私有成員 - 破壞封裝性 - 友元關(guān)系不可傳遞 - 友元關(guān)系的單向性 - 友元聲明的形式及數(shù)量不受限制

在類聲明的任何區(qū)域中聲明蹬叭,而定義則在類的外部藕咏。
friend <類型><友元函數(shù)名>(<參數(shù)表>);
注意,友元函數(shù)只是一個(gè)普通函數(shù)秽五,并不是該類的類成員函數(shù)孽查,它可以在任何地方調(diào)用,友元函數(shù)中通過(guò)對(duì)象名來(lái)訪問(wèn)該類的私有或保護(hù)成員坦喘。

#include <iostream>

using namespace std;

class A
{
public:
    A(int _a):a(_a){};
    friend int geta(A &ca);  ///< 友元函數(shù)
private:
    int a;
};

int geta(A &ca) 
{
    return ca.a;
}

int main()
{
    A a(3);    
    cout<<geta(a)<<endl;

    return 0;
}

友元類的聲明在該類的聲明中盲再,而實(shí)現(xiàn)在該類外。
friend class <友元類名>;
類B是類A的友元瓣铣,那么類B可以直接訪問(wèn)A的私有成員答朋。

#include <iostream>

using namespace std;

class A
{
public:
    A(int _a):a(_a){};
    friend class B;
private:
    int a;
};

class B
{
public:
    int getb(A ca) {
        return  ca.a; 
    };
};

int main() 
{
    A a(3);
    B b;
    cout<<b.getb(a)<<endl;
    return 0;
}

友元關(guān)系沒(méi)有繼承性 假如類B是類A的友元,類C繼承于類A棠笑,那么友元類B是沒(méi)辦法直接訪問(wèn)類C的私有或保護(hù)成員梦碗。

友元關(guān)系沒(méi)有傳遞性 假如類B是類A的友元,類C是類B的友元蓖救,那么友元類C是沒(méi)辦法直接訪問(wèn)類A的私有或保護(hù)成員洪规,也就是不存在“友元的友元”這種關(guān)系。

  • using
#include <iostream>
#define isNs1 1
//#define isGlobal 2
using namespace std;
void func() 
{
    cout<<"::func"<<endl;
}

namespace ns1 {
    void func()
    {
        cout<<"ns1::func"<<endl; 
    }
}

namespace ns2 {
#ifdef isNs1 
    using ns1::func;    /// ns1中的函數(shù)
#elif isGlobal
    using ::func; /// 全局中的函數(shù)
#else
    void func() 
    {
        cout<<"other::func"<<endl; 
    }
#endif
}

int main() 
{
    /**
     * 這就是為什么在c++中使用了cmath而不是math.h頭文件
     */
    ns2::func(); // 會(huì)根據(jù)當(dāng)前環(huán)境定義宏的不同來(lái)調(diào)用不同命名空間下的func()函數(shù)
    return 0;
}
class Base{
public:
 std::size_t size() const { return n;  }
protected:
 std::size_t n;
};
class Derived : private Base {
public:
 using Base::size;
protected:
 using Base::n;
};

在繼承過(guò)程中循捺,派生類可以覆蓋重載函數(shù)的0個(gè)或多個(gè)實(shí)例斩例,一旦定義了一個(gè)重載版本,那么其他的重載版本都會(huì)變?yōu)椴豢梢?jiàn)从橘。

如果對(duì)于基類的重載函數(shù)念赶,我們需要在派生類中修改一個(gè),又要讓其他的保持可見(jiàn)恰力,必須要重載所有版本叉谜,這樣十分的繁瑣。

#include <iostream>
using namespace std;

class Base{
    public:
        void f(){ cout<<"f()"<<endl;
        }
        void f(int n){
            cout<<"Base::f(int)"<<endl;
        }
};

class Derived : private Base {
    public:
        using Base::f;
        void f(int n){
            cout<<"Derived::f(int)"<<endl;
        }
};

int main()
{
    Base b;
    Derived d;
    d.f();
    d.f(1);
    return 0;
}

如上代碼中牺勾,在派生類中使用using聲明語(yǔ)句指定一個(gè)名字而不指定形參列表正罢,所以一條基類成員函數(shù)的using聲明語(yǔ)句就可以把該函數(shù)的所有重載實(shí)例添加到派生類的作用域中。此時(shí)驻民,派生類只需要定義其特有的函數(shù)就行了翻具,而無(wú)需為繼承而來(lái)的其他函數(shù)重新定義。

C中常用typedef A B這樣的語(yǔ)法回还,將B定義為A類型裆泳,也就是給A類型一個(gè)別名B

對(duì)應(yīng)typedef A B,使用using B=A可以進(jìn)行同樣的操作柠硕。

typedef vector<int> V1; 
using V2 = vector<int>;
  • 全局作用域符(::name):用于類型名稱(類工禾、類成員、成員函數(shù)蝗柔、變量等)前闻葵,表示作用域?yàn)槿置臻g

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

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

  • enum:
    傳統(tǒng)問(wèn)題:

    • 作用域不受限,會(huì)容易引起命名沖突。例如下面無(wú)法編譯通過(guò)的:
#include <iostream>
using namespace std;

enum Color {RED,BLUE};
enum Feeling {EXCITED,BLUE};

int main() 
{
    return 0;
}
  • 會(huì)隱式轉(zhuǎn)換為int
  • 用來(lái)表征枚舉變量的實(shí)際類型不能明確指定癣丧,從而無(wú)法支持枚舉類型的前向聲明槽畔。

解決作用域不受限帶來(lái)的命名沖突問(wèn)題的一個(gè)簡(jiǎn)單方法是,給枚舉變量命名時(shí)加前綴胁编,如上面例子改成 COLOR_BLUE 以及 FEELING_BLUE厢钧。

namespace Color 
{
    enum Type
    {
        RED=15,
        YELLOW,
        BLUE
    };
};

這樣之后就可以用 Color::Type c = Color::RED; 來(lái)定義新的枚舉變量了。如果 using namespace Color 后嬉橙,前綴還可以省去早直,使得代碼簡(jiǎn)化。不過(guò)市框,因?yàn)槊臻g是可以隨后被擴(kuò)充內(nèi)容的霞扬,所以它提供的作用域封閉性不高。在大項(xiàng)目中枫振,還是有可能不同人給不同的東西起同樣的枚舉類型名喻圃。

更“有效”的辦法是用一個(gè)類或結(jié)構(gòu)體來(lái)限定其作用域,例如:定義新變量的方法和上面命名空間的相同蒋得。不過(guò)這樣就不用擔(dān)心類在別處被修改內(nèi)容级及。這里用結(jié)構(gòu)體而非類,一是因?yàn)楸旧硐M@些常量可以公開(kāi)訪問(wèn)额衙,二是因?yàn)樗话瑪?shù)據(jù)沒(méi)有成員函數(shù)饮焦。

struct Color1
{
    enum Type
    {
        RED=102,
        YELLOW,
        BLUE
    };
};

C++11 標(biāo)準(zhǔn)中引入了“枚舉類”(enum class),可以較好地解決上述問(wèn)題窍侧。

  • 新的enum的作用域不在是全局的
  • 不能隱式轉(zhuǎn)換成其他類型
/**
 * @brief C++11的枚舉類
 * 下面等價(jià)于enum class Color2:int
 */
enum class Color2
{
    RED=2,
    YELLOW,
    BLUE
};
r2 c2 = Color2::RED;
cout << static_cast<int>(c2) << endl; //必須轉(zhuǎn)县踢!
  • 可以指定用特定的類型來(lái)存儲(chǔ)enum
enum class Color3:char;  // 前向聲明

// 定義
enum class Color3:char 
{
    RED='r',
    BLUE
};
char c3 = static_cast<char>(Color3::RED);
class Person{
public:
    typedef enum {
        BOY = 0,
        GIRL
    }SexType;
};
//訪問(wèn)的時(shí)候通過(guò),Person::BOY或者Person::GIRL來(lái)進(jìn)行訪問(wèn)伟件。
  • 枚舉常量不會(huì)占用對(duì)象的存儲(chǔ)空間硼啤,它們?cè)诰幾g時(shí)被全部求值。

  • 枚舉常量的缺點(diǎn)是:它的隱含數(shù)據(jù)類型是整數(shù)斧账,其最大值有限谴返,且不能表示浮點(diǎn)煞肾。

  • 這里的括號(hào)是必不可少的,decltype的作用是“查詢表達(dá)式的類型”,因此嗓袱,上面語(yǔ)句的效果是籍救,返回 expression 表達(dá)式的類型。注意渠抹,decltype 僅僅“查詢”表達(dá)式的類型蝙昙,并不會(huì)對(duì)表達(dá)式進(jìn)行“求值”。
decltype (expression)
int i = 4;
decltype(i) a; //推導(dǎo)結(jié)果為int梧却。a的類型為int奇颠。
using size_t = decltype(sizeof(0));//sizeof(a)的返回值為size_t類型
using ptrdiff_t = decltype((int*)0 - (int*)0);
using nullptr_t = decltype(nullptr);
vector<int >vec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin; i != vec.end(); i++)
{
//...
}
//在C++中,我們有時(shí)候會(huì)遇上一些匿名類型放航,如:
struct 
{
    int d ;
    doubel b;
}anon_s;
//而借助decltype烈拒,我們可以重新使用這個(gè)匿名的結(jié)構(gòu)體:

decltype(anon_s) as ;//定義了一個(gè)上面匿名的結(jié)構(gòu)體
泛型編程中結(jié)合auto,用于追蹤函數(shù)的返回值類型

這也是decltype最大的用途了三椿。
template <typename T>
auto multiply(T x, T y)->decltype(x*y)
{
    return x*y;
}

對(duì)于decltype(e)而言缺菌,其判別結(jié)果受以下條件的影響:

如果e是一個(gè)沒(méi)有帶括號(hào)的標(biāo)記符表達(dá)式或者類成員訪問(wèn)表達(dá)式,那么的decltype(e)就是e所命名的實(shí)體的類型搜锰。此外伴郁,如果e是一個(gè)被重載的函數(shù),則會(huì)導(dǎo)致編譯錯(cuò)誤蛋叼。 否則 焊傅,假設(shè)e的類型是T,如果e是一個(gè)將亡值狈涮,那么decltype(e)為T(mén)&& 否則狐胎,假設(shè)e的類型是T,如果e是一個(gè)左值歌馍,那么decltype(e)為T(mén)&握巢。 否則,假設(shè)e的類型是T松却,則decltype(e)為T(mén)暴浦。

標(biāo)記符指的是除去關(guān)鍵字、字面量等編譯器需要使用的標(biāo)記之外的程序員自己定義的標(biāo)記晓锻,而單個(gè)標(biāo)記符對(duì)應(yīng)的表達(dá)式即為標(biāo)記符表達(dá)式歌焦。例如:
int arr[4]
則arr為一個(gè)標(biāo)記符表達(dá)式,而arr[3]+0不是砚哆。

int i = 4;
int arr[5] = { 0 };
int *ptr = arr;
struct S{ double d; }s ;
void Overloaded(int);
void Overloaded(char);//重載的函數(shù)
int && RvalRef();
const bool Func(int);

//規(guī)則一:推導(dǎo)為其類型
decltype (arr) var1; //int 標(biāo)記符表達(dá)式

decltype (ptr) var2;//int *  標(biāo)記符表達(dá)式

decltype(s.d) var3;//doubel 成員訪問(wèn)表達(dá)式

//decltype(Overloaded) var4;//重載函數(shù)独撇。編譯錯(cuò)誤。

//規(guī)則二:將亡值。推導(dǎo)為類型的右值引用纷铣。

decltype (RvalRef()) var5 = 1;

//規(guī)則三:左值卵史,推導(dǎo)為類型的引用。

decltype ((i))var6 = i;     //int&

decltype (true ? i : i) var7 = i; //int&  條件表達(dá)式返回左值关炼。

decltype (++i) var8 = i; //int&  ++i返回i的左值程腹。

decltype(arr[5]) var9 = i;//int&. []操作返回左值

decltype(*ptr)var10 = i;//int& *操作返回左值

decltype("hello")var11 = "hello"; //const char(&)[9]  字符串字面常量為左值匣吊,且為const左值儒拂。


//規(guī)則四:以上都不是,則推導(dǎo)為本類型

decltype(1) var12;//const int

decltype(Func(1)) var13=true;//const bool

decltype(i++) var14 = i;//int i++返回右值
  • 右值引用可實(shí)現(xiàn)轉(zhuǎn)移語(yǔ)義(Move Sementics)和精確傳遞(Perfect Forwarding)色鸳,它的主要目的有兩個(gè)方面:

    1.消除兩個(gè)對(duì)象交互時(shí)不必要的對(duì)象拷貝社痛,節(jié)省運(yùn)算存儲(chǔ)資源,提高效率命雀。
    2.能夠更簡(jiǎn)潔明確地定義泛型函數(shù)蒜哀。

引用折疊
  • X& &X& &&吏砂、X&& & 可折疊成 X&
  • X&& && 可折疊成 X&&
  • C++的引用在減少了程序員自由度的同時(shí)提升了內(nèi)存操作的安全性和語(yǔ)義的優(yōu)美性撵儿。

  • private 是完全私有的,只有當(dāng)前類中的成員能訪問(wèn)到.

  • protected 是受保護(hù)的,只有當(dāng)前類的成員與繼承該類的類才能訪問(wèn).

  • 在使用new的時(shí)候做了兩件事:

1、調(diào)用operator new分配空間

2狐血、調(diào)用構(gòu)造函數(shù)初始化對(duì)象

  • 在使用delete的時(shí)候也做了兩件事:

1淀歇、調(diào)用析構(gòu)函數(shù)清理對(duì)象

2、調(diào)用operator delete函數(shù)釋放空間

  • 在使用new[N]的時(shí)候也做了兩件事:

1匈织、調(diào)用operator new分配空間

2浪默、調(diào)用N次構(gòu)造函數(shù)初始化N個(gè)對(duì)象

  • 在使用delete[]的時(shí)候也做了兩件事:

1、調(diào)用N次析構(gòu)函數(shù)清理N個(gè)對(duì)象

2缀匕、調(diào)用operator delete函數(shù)釋放空間

  • 1.1 字符串化操作符(#)

在一個(gè)宏中的參數(shù)前面使用一個(gè)#,預(yù)處理器會(huì)把這個(gè)參數(shù)轉(zhuǎn)換為一個(gè)字符數(shù)組纳决,換言之就是:#是“字符串化”的意思,出現(xiàn)在宏定義中的#是把跟在后面的參數(shù)轉(zhuǎn)換成一個(gè)字符串乡小。

  • 1.2 符號(hào)連接操作符(##)

“##”是一種分隔連接方式阔加,它的作用是先分隔,然后進(jìn)行強(qiáng)制連接满钟。將宏定義的多個(gè)形參轉(zhuǎn)換成一個(gè)實(shí)際參數(shù)名胜榔。

注意事項(xiàng):

(1)當(dāng)用##連接形參時(shí),##前后的空格可有可無(wú)零远。
(2)連接后的實(shí)際參數(shù)名苗分,必須為實(shí)際存在的參數(shù)名或是編譯器已知的宏定義。
(3)如果##后的參數(shù)本身也是一個(gè)宏的話牵辣,##會(huì)阻止這個(gè)宏的展開(kāi)摔癣。

  • 1.3 續(xù)行操作符(\

當(dāng)定義的宏不能用一行表達(dá)完整時(shí),可以用”\”表示下一行繼續(xù)此宏的定義。
注意 \ 前留空格择浊。

template<typename ...Args>
class A {
private:
    int size = 0;    // c++11 支持類內(nèi)初始化
public:
    A() {
        size = sizeof...(Args);
        cout << size << endl;
    }
};

 A<int, string, vector<int>> a;    // 類型任意

 // Tuple就是利用這個(gè)特性(變長(zhǎng)參數(shù)模板)
tuple<int, string> t = make_tuple(1, "hha");

單例模式

//簡(jiǎn)單實(shí)現(xiàn)戴卜,只適用于單線程下。
//懶漢 線程不安全
class singleton {
private:
    singleton() {}
    static singleton *p;
public:
    static singleton *instance();
};

singleton *singleton::p = nullptr;

singleton* singleton::instance() {
    if (p == nullptr)
        p = new singleton();
    return p;
}
//餓漢 這個(gè)是線程安全的
class singleton {
private:
    singleton() {}
    static singleton *p;
public:
    static singleton *instance();
};

singleton *singleton::p = new singleton();
singleton* singleton::instance() {
    return p;
}

//多線程下單例模式
class singleton {
private:
    singleton() {}
    static singleton *p;
    static mutex lock_;
public:
    static singleton *instance();
};

singleton *singleton::p = nullptr;

singleton* singleton::instance() {
    lock_guard<mutex> guard(lock_);
    if (p == nullptr)
        p = new singleton();
    return p;
}
//雙重檢查鎖+自動(dòng)回收
class singleton {
private:
    singleton() {}

    static singleton *p;
    static mutex lock_;
public:
    singleton *instance();

    // 實(shí)現(xiàn)一個(gè)內(nèi)嵌垃圾回收類
    class CGarbo
    {
    public:
        ~CGarbo()
        {
            if(singleton::p)
                delete singleton::p;
        }
    };
    static CGarbo Garbo; // 定義一個(gè)靜態(tài)成員變量琢岩,程序結(jié)束時(shí)投剥,系統(tǒng)會(huì)自動(dòng)調(diào)用它的析構(gòu)函數(shù)從而釋放單例對(duì)象
};

singleton *singleton::p = nullptr;
singleton::CGarbo Garbo;

singleton* singleton::instance() {
    if (p == nullptr) {
        lock_guard<mutex> guard(lock_);
        if (p == nullptr)
            p = new singleton();
    }
    return p;
}

5.memory barrier指令

DCLP問(wèn)題在C++11中,這個(gè)問(wèn)題得到了解決担孔。

因?yàn)樾碌腃++11規(guī)定了新的內(nèi)存模型江锨,保證了執(zhí)行上述3個(gè)步驟的時(shí)候不會(huì)發(fā)生線程切換,相當(dāng)這個(gè)初始化過(guò)程是“原子性”的的操作糕篇,DCL又可以正確使用了啄育,不過(guò)在C++11下卻有更簡(jiǎn)潔的多線程singleton寫(xiě)法了,這個(gè)留在后面再介紹拌消。

C++11之前解決方法是barrier指令挑豌。要使其正確執(zhí)行的話,就得在步驟2墩崩、3直接加上一道m(xù)emory barrier氓英。強(qiáng)迫CPU執(zhí)行的時(shí)候按照1、2鹦筹、3的步驟來(lái)運(yùn)行铝阐。

第一種實(shí)現(xiàn):

基于operator new+placement new,遵循1,2,3執(zhí)行順序依次編寫(xiě)代碼盛龄。

// method 1 operator new + placement new
singleton *instance() {
    if (p == nullptr) {
        lock_guard<mutex> guard(lock_);
        if (p == nullptr) {
            singleton *tmp = static_cast<singleton *>(operator new(sizeof(singleton)));
            new(p)singleton();
            p = tmp;
        }
    }
    return p;
}

**第二種實(shí)現(xiàn):**

基于直接嵌入ASM匯編指令mfence饰迹,uninx的barrier宏也是通過(guò)該指令實(shí)現(xiàn)的。

#define barrier() __asm__ volatile ("lwsync")
singleton *singleton::instance() {
    if (p == nullptr) {
        lock_guard<mutex> guard(lock_);
        barrier();
        if (p == nullptr) {
            p = new singleton();
        }
    }
    return p;
}

通常情況下是調(diào)用cpu提供的一條指令余舶,這條指令的作用是會(huì)阻止cpu將該指令之前的指令交換到該指令之后啊鸭,這條指令也通常被叫做barrier。 上面代碼中的asm表示這個(gè)是一條匯編指令匿值,volatile是可選的赠制,如果用了它,則表示向編譯器聲明不允許對(duì)該匯編指令進(jìn)行優(yōu)化挟憔。lwsync是POWERPC提供的barrier指令钟些。

  • Scott Meyer在《Effective C++》中提出了一種簡(jiǎn)潔的singleton寫(xiě)法
singleton *singleton::instance() {
    static singleton p;
    return &p;
}
mutex singleton::lock_;
atomic<singleton *> singleton::p;

/*
* std::atomic_thread_fence(std::memory_order_acquire); 
* std::atomic_thread_fence(std::memory_order_release);
* 這兩句話可以保證他們之間的語(yǔ)句不會(huì)發(fā)生亂序執(zhí)行。
*/
singleton *singleton::instance() {
    singleton *tmp = p.load(memory_order_relaxed);
    atomic_thread_fence(memory_order_acquire);
    if (tmp == nullptr) {
        lock_guard<mutex> guard(lock_);
        tmp = p.load(memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new singleton();
            atomic_thread_fence(memory_order_release);
            p.store(tmp, memory_order_relaxed);
        }
    }
    return p;
}

值得注意的是绊谭,上述代碼使用兩個(gè)比較關(guān)鍵的術(shù)語(yǔ)政恍,獲得與釋放:

  • 獲得是一個(gè)對(duì)內(nèi)存的讀操作,當(dāng)前線程的任何后面的讀寫(xiě)操作都不允許重排到這個(gè)操作的前面去达传。
  • 釋放是一個(gè)對(duì)內(nèi)存的寫(xiě)操作篙耗,當(dāng)前線程的任何前面的讀寫(xiě)操作都不允許重排到這個(gè)操作的后面去迫筑。

如果是在unix平臺(tái)的話,除了使用atomic operation外宗弯,在不適用C++11的情況下脯燃,還可以通過(guò)pthread_once來(lái)實(shí)現(xiàn)Singleton。

原型如下:
int pthread_once(pthread_once_t once_control, void (init_routine) (void))蒙保;

class singleton {
private:
    singleton(); //私有構(gòu)造函數(shù)辕棚,不允許使用者自己生成對(duì)象
    singleton(const singleton &other);

    //要寫(xiě)成靜態(tài)方法的原因:類成員函數(shù)隱含傳遞this指針(第一個(gè)參數(shù))
    static void init() {
        p = new singleton();
    }

    static pthread_once_t ponce_;
    static singleton *p; //靜態(tài)成員變量 
public:
    singleton *instance() {
        // init函數(shù)只會(huì)執(zhí)行一次
        pthread_once(&ponce_, &singleton::init);
        return p;
    }
};
image
  • C++ tr1全稱Technical Report 1,是針對(duì)C++標(biāo)準(zhǔn)庫(kù)的第一次擴(kuò)展邓厕。即將到來(lái)的下一個(gè)版本的C++標(biāo)準(zhǔn)c++0x會(huì)包括它逝嚎,以及一些語(yǔ)言本身的擴(kuò)充。tr1包括大家期待已久的smart pointer邑狸,正則表達(dá)式以及其他一些支持范型編程的內(nèi)容懈糯。草案階段,新增的類和模板的名字空間是std::tr1单雾。
#include <tr1/array>
std::tr1::array<int ,10> a;//第一個(gè)類型,第二個(gè)大小
  • deque與vector最大的差異就是:

    1.deque允許于常數(shù)時(shí)間內(nèi)對(duì)頭端進(jìn)行插入或刪除元素她紫;

    2.deque是分段連續(xù)線性空間硅堆,隨時(shí)可以增加一段新的空間;

  • 用戶看起來(lái)deque使用的是連續(xù)空間贿讹,實(shí)際上是分段連續(xù)線性空間渐逃。為了管理分段空間deque容器引入了map,稱之為中控器民褂,map是一塊連續(xù)的空間茄菊,其中每個(gè)元素是指向緩沖區(qū)的指針,緩沖區(qū)才是deque存儲(chǔ)數(shù)據(jù)的主體赊堪。


    image

    在上圖中面殖,buffer稱為緩沖區(qū),顯示map size的一段連續(xù)空間就是中控器哭廉。
    中控器包含了map size,指向buffer的指針脊僚,deque的開(kāi)始迭代器與結(jié)尾迭代器。

_Tp     **_M_map;
size_t      _M_map_size;
iterator    _M_start;
iterator    _M_finish;

deque是使用基類_Deque_base來(lái)完成內(nèi)存管理與中控器管理

  • 在stack的源碼中我們關(guān)注兩點(diǎn): - 默認(rèn)_Sequence為deque - 內(nèi)部函數(shù)實(shí)現(xiàn)是調(diào)用_Sequence對(duì)應(yīng)容器的函數(shù)遵绰。

  • 對(duì)于stack來(lái)說(shuō)辽幌,底層容器可以是vector、deque椿访、list乌企,但不可以是map、set成玫。 由于編譯器不會(huì)做全面性檢查加酵,當(dāng)調(diào)用函數(shù)不存在的時(shí)候端辱,就編譯不通過(guò),所以對(duì)于像set雖然不能作為底層容器虽画,但如果具有某些函數(shù)舞蔽,調(diào)用仍然是成功的,直到調(diào)用的函數(shù)不存在码撰。

  • 在queue的源碼中我們關(guān)注兩點(diǎn): - 默認(rèn)_Sequence為deque - 內(nèi)部函數(shù)實(shí)現(xiàn)是調(diào)用_Sequence對(duì)應(yīng)容器的函數(shù)渗柿。

  • 優(yōu)先隊(duì)列則是使用vector作為默認(rèn)容器。

  • 對(duì)于queue底層容器可以是deque脖岛,也可以是list朵栖,但不能是vector,map,set,使用默認(rèn)的deque效率在插入方面比其他容器作為底層要快柴梆!

  • 對(duì)于優(yōu)先隊(duì)列來(lái)說(shuō)陨溅,測(cè)試結(jié)果發(fā)現(xiàn),采用deque要比默認(rèn)的vector插入速度快绍在! 底層支持vector门扇、deque容器,但不支持list偿渡、map臼寄、set。

  • stack溜宽、queue吉拳、priority_queue不被稱為容器, 把它稱為容器配接器适揉。

  • vector的數(shù)據(jù)安排以及操作方式留攒,與array非常相似。兩者的唯一差別在于空間的運(yùn)用的靈活性嫉嘀,array是靜態(tài)的炼邀,一旦配置了就不能改變,而 vector是動(dòng)態(tài)空間吃沪,隨著元素的加入汤善,它的內(nèi)部機(jī)制會(huì)自行擴(kuò)充空間以容納新元素。

  • 類作用域

在類外部訪問(wèn)類中的名稱時(shí)票彪,可以使用類作用域操作符红淡,形如MyClass::name的調(diào)用通常存在三種:靜態(tài)數(shù)據(jù)成員、靜態(tài)成員函數(shù)和嵌套類型

struct MyClass {
    static int A; //靜態(tài)成員
    static int B(){cout<<"B()"<<endl; return 100;} //靜態(tài)函數(shù)
    typedef int C;  //嵌套類型
    struct A1 { //嵌套類型
        static int s;
    };
};
image
  • 一種能夠順序訪問(wèn)容器中每個(gè)元素的方法降铸,使用該方法不能暴露容器內(nèi)部的表達(dá)方式在旱。而類型萃取技術(shù)就是為了要解決和 iterator 有關(guān)的問(wèn)題的。

  • 總結(jié):通過(guò)定義內(nèi)嵌類型推掸,我們獲得了知曉 iterator 所指元素類型的方法桶蝎,通過(guò) traits 技法驻仅,我們將函數(shù)模板對(duì)于原生指針和自定義 iterator 的定義都統(tǒng)一起來(lái),我們使用 traits 技法主要是為了解決原生指針和自定義 iterator 之間的不同所造成的代碼冗余登渣,這就是 traits 技法的妙處所在噪服。

  • 因?yàn)榭疹愅瑯涌梢员粚?shí)例化,每個(gè)實(shí)例在內(nèi)存中都有一個(gè)獨(dú)一無(wú)二的地址胜茧,為了達(dá)到這個(gè)目的粘优,編譯器往往會(huì)給一個(gè)空類隱含的加一個(gè)字節(jié),這樣空類在實(shí)例化后在內(nèi)存得到了獨(dú)一無(wú)二的地址.所以上述大小為1.

  • 兩個(gè)不同對(duì)象的地址不同呻顽。

  • 基類為空雹顺,通過(guò)繼承方式來(lái)獲得基類的功能,并沒(méi)有產(chǎn)生額外大小的優(yōu)化稱之為EBO(空基類優(yōu)化)廊遍。

  • 第一種方式的內(nèi)存管理:嵌入一個(gè)內(nèi)存管理類

template<class T, class Allocator>
class MyContainerNotEBO {
    T *data_ = nullptr;
    std::size_t capacity_;
    Allocator allocator_;   // 嵌入一個(gè)MyAllocator
public:
    MyContainerNotEBO(std::size_t capacity)
            : capacity_(capacity), allocator_(), data_(nullptr) {
        std::cout << "alloc malloc" << std::endl;
        data_ = reinterpret_cast<T *>(allocator_.allocate(capacity * sizeof(T))); // 分配內(nèi)存
    }

    ~MyContainerNotEBO() {
        std::cout << "MyContainerNotEBO free malloc" << std::endl;
        allocator_.deallocate(data_);
    }
};
  • 第二種方式:采用空基類優(yōu)化嬉愧,繼承來(lái)獲得內(nèi)存管理功能
template<class T, class Allocator>
class MyContainerEBO
        : public Allocator {    // 繼承一個(gè)EBO
    T *data_ = nullptr;
    std::size_t capacity_;
public:
    MyContainerEBO(std::size_t capacity)
            : capacity_(capacity), data_(nullptr) {
        std::cout << "alloc malloc" << std::endl;
        data_ = reinterpret_cast<T *>(this->allocate(capacity * sizeof(T)));
    }

    ~MyContainerEBO() {
        std::cout << "MyContainerEBO free malloc" << std::endl;
        this->deallocate(data_);
    }
};

int main() {
    MyContainerNotEBO<int, MyAllocator> notEbo = MyContainerNotEBO<int, MyAllocator>(0);
    std::cout << "Using Not EBO Test sizeof is " << sizeof(notEbo) << std::endl;
    MyContainerEBO<int, MyAllocator> ebo = MyContainerEBO<int, MyAllocator>(0);
    std::cout << "Using EBO Test sizeof is " << sizeof(ebo) << std::endl;

    return 0;
}

//結(jié)果
alloc malloc
Using Not EBO Test sizeof is 24
alloc malloc
Using EBO Test sizeof is 16
MyContainerEBO free malloc
MyContainerNotEBO free malloc
  • 采用EBO的設(shè)計(jì)確實(shí)比嵌入設(shè)計(jì)好很多。

? 二分查找的速度比簡(jiǎn)單查找快得多喉前。
? O(log n)比O(n)快没酣。需要搜索的元素越多,前者比后者就快得越多被饿。
? 算法運(yùn)行時(shí)間并不以秒為單位四康。
? 算法運(yùn)行時(shí)間是從其增速的角度度量的。
? 算法運(yùn)行時(shí)間用大O表示法表示狭握。

  • set/multiset以rb_tree為底層結(jié)構(gòu),因此有元素自動(dòng)排序特性疯溺。排序的依據(jù)是key论颅,而set/multiset元素的value和key合二為一:value就是key。

  • 第一個(gè)問(wèn)題:key是value,value也是key囱嫩。

  • 第二個(gè)問(wèn)題:無(wú)法使用迭代器改變?cè)刂怠?/p>

  • 第三個(gè)問(wèn)題:插入是唯一的key恃疯。

cout<<"flag: "<<itree._M_insert_unique(5).second<<endl;  // 學(xué)習(xí)返回值
typedef pair<int ,bool> _Res;    // 也來(lái)用一下typedef后的pair
cout<<_Res(1,true).first<<endl;  // 直接包裹
_Res r=make_pair(2,false);    // 定義新對(duì)象
cout<<r.first<<endl;   // 輸出結(jié)果
  • map的key為key,value為key+data,與set是不同的,set是key就是value墨闲,value就是key今妄。
  • map的key不可修改,map與multimap的插入調(diào)用函數(shù)不同,影響了其key是否對(duì)應(yīng)value鸳碧。
  • initializer_list使用
  • map有[]操作符盾鳞,而multimap沒(méi)有[]操作符。
insert的幾種方法:

(1) 插入 pair


std::pair<iterator, bool> insert(const value_type& __x)
{ return _M_t._M_insert_unique(__x); }
map里面

(2) 在指定位置瞻离,插入pair


iterator insert(iterator __position, const value_type& __x)
{ return _M_t._M_insert_equal_(__position, __x); }
(3) 從一個(gè)范圍進(jìn)行插入


template<typename _InputIterator>
void
insert(_InputIterator __first, _InputIterator __last)
{ _M_t._M_insert_equal(__first, __last); }
(4)從list中插入


void
insert(initializer_list<value_type> __l)
{ this->insert(__l.begin(), __l.end()); }
針對(duì)最后一個(gè)insert腾仅,里面有個(gè)initializer_list,舉個(gè)例子大家就知道了套利。
  • 結(jié)論1:undered_map與undered_set不允許key重復(fù),而帶multi的則允許key重復(fù);
  • 結(jié)論2:undered_map與undered_multimap采用的迭代器是iterator姨夹,而undered_set與undered_multiset采用的迭代器是const_iterator赞警。
  • 結(jié)論3:undered_map與undered_multimap的key是key,value是key+value稿黄;而undered_set與undered_multiset的key是Value,Value也是Key跌造。

五種創(chuàng)建線程的方式

  • 函數(shù)指針
  • Lambda函數(shù)吧
  • Functor(仿函數(shù))
  • 非靜態(tài)成員函數(shù)
  • 靜態(tài)成員函數(shù)

2.1 函數(shù)指針

// 1.函數(shù)指針
void fun(int x) {
    while (x-- > 0) {
        cout << x << endl;
    }
}
// 調(diào)用
std::thread t1(fun, 10);
t1.join();

2.2 Lambda函數(shù)

// 注意:如果我們創(chuàng)建多線程 并不會(huì)保證哪一個(gè)先開(kāi)始
int main() {
    // 2.Lambda函數(shù)
    auto fun = [](int x) {
        while (x-- > 0) {
            cout << x << endl;
        }
    };
//    std::1.thread t1(fun, 10);
    // 也可以寫(xiě)成下面:
    std::thread t1_1([](int x) {
        while (x-- > 0) {
            cout << x << endl;
        }
    }, 11);
//    std::1.thread t2(fun, 10);
//    t1.join();
    t1_1.join();
//    t2.join();
    return 0;
}

2.3 仿函數(shù)

// 3.functor (Funciton Object)
class Base {
public:
    void operator()(int x) {
        while (x-- > 0) {
            cout << x << endl;
        }
    }
};
// 調(diào)用
thread t(Base(), 10);
t.join();

2.4 非靜態(tài)成員函數(shù)

// 4.Non-static member function
class Base {
public:
    void fun(int x) {
        while (x-- > 0) {
            cout << x << endl;
        }
    }
};
// 調(diào)用
thread t(&Base::fun,&b, 10);
t.join();

2.5 靜態(tài)成員函數(shù)

// 4.Non-static member function
class Base {
public:
    static void fun(int x) {
        while (x-- > 0) {
            cout << x << endl;
        }
    }
};
// 調(diào)用
thread t(&Base::fun, 10);
t.join();

join

  • 一旦線程開(kāi)始杆怕,我們要想等待線程完成,需要在該對(duì)象上調(diào)用join()
  • 雙重join將導(dǎo)致程序終止
  • 在join之前我們應(yīng)該檢查顯示是否可以被join,通過(guò)使用joinable()
void run(int count) {
    while (count-- > 0) {
        cout << count << endl;
    }
    std::this_thread::sleep_for(chrono::seconds(3));
}

int main() {
    thread t1(run, 10);
    cout << "main()" << endl;
    t1.join();
    if (t1.joinable()) {
        t1.join();
    }
    cout << "main() after" << endl;
    return 0;
}

detach

  • 這用于從父線程分離新創(chuàng)建的線程
  • 在分離線程之前鼻听,請(qǐng)務(wù)必檢查它是否可以joinable财著,否則可能會(huì)導(dǎo)致兩次分離,并且雙重detach()將導(dǎo)致程序終止
  • 如果我們有分離的線程并且main函數(shù)正在返回撑碴,那么分離的線程執(zhí)行將被掛起
void run(int count) {
    while (count-- > 0) {
        cout << count << endl;
    }
    std::this_thread::sleep_for(chrono::seconds(3));
}

int main() {
    thread t1(run, 10);
    cout << "main()" << endl;
    t1.detach();
    if(t1.joinable())
        t1.detach();
    cout << "main() after" << endl;
    return 0;
  • 增加變量(i ++)的過(guò)程分三個(gè)步驟:

      1.將內(nèi)存內(nèi)容復(fù)制到CPU寄存器撑教。 load
      2.在CPU中增加該值。 increment
      3.將新值存儲(chǔ)在內(nèi)存中醉拓。 store
    
  • 如果只能通過(guò)一個(gè)線程訪問(wèn)該內(nèi)存位置(例如下面的變量i)伟姐,則不會(huì)出現(xiàn)爭(zhēng)用情況,也沒(méi)有與i關(guān)聯(lián)的臨界區(qū)亿卤。 但是sum變量是一個(gè)全局變量愤兵,可以通過(guò)兩個(gè)線程進(jìn)行訪問(wèn)。 兩個(gè)線程可能會(huì)嘗試同時(shí)增加變量排吴。
#include <iostream>
#include <mutex>
#include <thread>

using namespace std;

int sum = 0; //shared

mutex m;

void *countgold() {
    int i; //local to each thread
    for (i = 0; i < 10000000; i++) {
        sum += 1;
    }
    return NULL;
}

int main() {
    thread t1(countgold);
    thread t2(countgold);

    //Wait for both threads to finish
    t1.join();
    t2.join();

    cout << "sum = " << sum << endl;
    return 0;
}

初始化列表與賦值

  • const成員的初始化只能在構(gòu)造函數(shù)初始化列表中進(jìn)行
  • 引用成員的初始化也只能在構(gòu)造函數(shù)初始化列表中進(jìn)行
  • 對(duì)象成員(對(duì)象成員所對(duì)應(yīng)的類沒(méi)有默認(rèn)構(gòu)造函數(shù))的初始化秆乳,也只能在構(gòu)造函數(shù)初始化列表中進(jìn)行

類之間嵌套

第一種: 使用初始化列表。

class Animal {
public:
    Animal() {
        std::cout << "Animal() is called" << std::endl;
    }

    Animal(const Animal &) {
        std::cout << "Animal (const Animal &) is called" << std::endl;
    }

    Animal &operator=(const Animal &) {
        std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
        return *this;
    }

    ~Animal() {
        std::cout << "~Animal() is called" << std::endl;
    }
};

class Dog {
public:
    Dog(const Animal &animal) : __animal(animal) {
        std::cout << "Dog(const Animal &animal) is called" << std::endl;
    }

    ~Dog() {
        std::cout << "~Dog() is called" << std::endl;
    }

private:
    Animal __animal;
};

int main() {
    Animal animal;
    std::cout << std::endl;
    Dog d(animal);
    std::cout << std::endl;
    return 0;
}

第二種:構(gòu)造函數(shù)賦值來(lái)初始化對(duì)象钻哩。

Dog(const Animal &animal) {
    __animal = animal;
    std::cout << "Dog(const Animal &animal) is called" << std::endl;
}
  • 類中包含其他自定義的class或者struct屹堰,采用初始化列表,實(shí)際上就是創(chuàng)建對(duì)象同時(shí)并初始化
  • 而采用類中賦值方式街氢,等價(jià)于先定義對(duì)象扯键,再進(jìn)行賦值,一般會(huì)先調(diào)用默認(rèn)構(gòu)造珊肃,在調(diào)用=操作符重載函數(shù)荣刑。
無(wú)默認(rèn)構(gòu)造函數(shù)的繼承關(guān)系中
class Animal {
public:
    Animal(int age) {
        std::cout << "Animal(int age) is called" << std::endl;
    }

    Animal(const Animal & animal) {
        std::cout << "Animal (const Animal &) is called" << std::endl;
    }

    Animal &operator=(const Animal & amimal) {
        std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
        return *this;
    }

    ~Animal() {
        std::cout << "~Animal() is called" << std::endl;
    }
};

class Dog : Animal {
public:
    Dog(int age) : Animal(age) {
        std::cout << "Dog(int age) is called" << std::endl;
    }

    ~Dog() {
        std::cout << "~Dog() is called" << std::endl;
    }

};
  • 由于在Animal中沒(méi)有默認(rèn)構(gòu)造函數(shù),所以報(bào)錯(cuò)伦乔,遇到這種問(wèn)題屬于災(zāi)難性的厉亏,我們應(yīng)該盡量避免,可以通過(guò)初始化列表給基類的構(gòu)造初始化评矩。

類中const數(shù)據(jù)成員叶堆、引用數(shù)據(jù)成員

  • 特別是引用數(shù)據(jù)成員,必須用初始化列表初始化斥杜,而不能通過(guò)賦值初始化虱颗!
class Animal {
public:
    Animal(int age, std::string name) : age_(age), name_(name) {
    std::cout << "Animal(int age) is called" << std::endl;
}
private:
    int &age_;
    const std::string name_;
};
// enum class
enum class EntityType {
    Ground = 0,
    Human,
    Aerial,
    Total
};

void foo(EntityType entityType)
{
    if (entityType == EntityType::Ground) {
        /*code*/
    }
}
  • 可以指定對(duì)象具有構(gòu)造函數(shù)和析構(gòu)函數(shù)沥匈,這些構(gòu)造函數(shù)和析構(gòu)函數(shù)在適當(dāng)?shù)臅r(shí)候由
  • 編譯器自動(dòng)調(diào)用,這為管理給定對(duì)象的內(nèi)存提供了更為方便的方法忘渔。
    • 資源在析構(gòu)函數(shù)中被釋放
    • 該類的實(shí)例是堆棧分配的
    • 資源是在構(gòu)造函數(shù)中獲取的高帖。

RAII代表“資源獲取是初始化”。常見(jiàn)的例子有:

  • 文件操作

  • 智能指針

  • 互斥量

  • 為了使用copy-swap畦粮,我們需要三件事:
    1.一個(gè)有效的拷貝構(gòu)造函數(shù)
    2.一個(gè)有效的析構(gòu)函數(shù)(兩者都是任何包裝程序的基礎(chǔ)散址,因此無(wú)論如何都應(yīng)完整)以及交換功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宣赔,一起剝皮案震驚了整個(gè)濱河市预麸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌儒将,老刑警劉巖吏祸,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異钩蚊,居然都是意外死亡贡翘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)砰逻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鸣驱,“玉大人,你說(shuō)我怎么就攤上這事蝠咆∮欢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵刚操,是天一觀的道長(zhǎng)递胧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)赡茸,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任祝闻,我火速辦了婚禮占卧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘联喘。我一直安慰自己华蜒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布豁遭。 她就那樣靜靜地躺著叭喜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蓖谢。 梳的紋絲不亂的頭發(fā)上捂蕴,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天譬涡,我揣著相機(jī)與錄音,去河邊找鬼啥辨。 笑死涡匀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的溉知。 我是一名探鬼主播陨瘩,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼级乍!你這毒婦竟也來(lái)了舌劳?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤玫荣,失蹤者是張志新(化名)和其女友劉穎甚淡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體崇决,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡材诽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恒傻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脸侥。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖盈厘,靈堂內(nèi)的尸體忽然破棺而出睁枕,到底是詐尸還是另有隱情,我是刑警寧澤沸手,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布外遇,位于F島的核電站,受9級(jí)特大地震影響契吉,放射性物質(zhì)發(fā)生泄漏跳仿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一捐晶、第九天 我趴在偏房一處隱蔽的房頂上張望菲语。 院中可真熱鬧,春花似錦惑灵、人聲如沸山上。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)佩憾。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妄帘,已是汗流浹背楞黄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寄摆,地道東北人谅辣。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像婶恼,于是被迫代替她去往敵國(guó)和親桑阶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容