C++PLUS

未完每日更新

https://blog.csdn.net/yawdd/article/details/80010148

C++中的內(nèi)存管理

計(jì)算機(jī)存儲(chǔ)體系
寄存器:

CPU內(nèi)部主要由控制器卧檐、運(yùn)算器和寄存器組成。
控制器負(fù)責(zé)指令的讀取和調(diào)度写半,運(yùn)算器負(fù)責(zé)指令的運(yùn)算執(zhí)行,寄存器負(fù)責(zé)數(shù)據(jù)的存儲(chǔ),它們之間通過CPU的內(nèi)部總線連接在一起。

寄存器擁有非常高的讀寫速度匆帚,可以看作數(shù)據(jù)在CPU內(nèi)的一個(gè)臨時(shí)存儲(chǔ)的單元。

寄存器的制作難度大轩猩,選材精卷扮,而且是集成到芯片內(nèi)部,所價(jià)格高均践。而內(nèi)存的成本則相對(duì)低廉晤锹,而且從工藝上來說,我們不可能在CPU內(nèi)部集成大量的存儲(chǔ)單元彤委。


高度緩存Cache

當(dāng)程序在運(yùn)行時(shí)鞭铆,就可以預(yù)先將部分在內(nèi)存中要執(zhí)行的指令代碼以及數(shù)據(jù)復(fù)制到高速緩存中去,而CPU則不再每次都從內(nèi)存中讀取指令而是直接從高速緩存依次讀取指令來執(zhí)行,從而加快了整體的速度车遂。

高速緩存又分為一級(jí)Cache和二級(jí)Cache封断,一級(jí)緩存集成在CPU內(nèi)部,二級(jí)緩存以前焊在主板上,現(xiàn)在也都集成在CPU內(nèi)部舶担。

Cache成本比寄存器低坡疼,但是比內(nèi)存的制造成本高,容量要比寄存器大衣陶,但是比內(nèi)存的容量小很多柄瑰。


內(nèi)存

分為只讀存儲(chǔ)器(ROM)、隨機(jī)存儲(chǔ)器(RAM)和高速緩存存儲(chǔ)器(cache)剪况。
內(nèi)存具有“掉電信息全部消失”的特性教沾,而外存則具有“掉電信息也不會(huì)丟失”的特性。


空間換時(shí)間

在軟件設(shè)計(jì)上有一個(gè)所謂的空間換時(shí)間的概念译断,就是當(dāng)兩個(gè)對(duì)象之間進(jìn)行交互時(shí)因?yàn)槎咛幚硭俣炔⒉灰恢聲r(shí)授翻,我們就需要引入緩存來解決讀寫不一致的問題。
比如文件讀寫或者socket通信時(shí)孙咪,因?yàn)镮O設(shè)備的處理速度很慢堪唐,所以在進(jìn)行文件讀寫以及socket通信時(shí)總是要將讀出或者寫入的部分?jǐn)?shù)據(jù)先保存到一個(gè)緩存中,然后再統(tǒng)一的執(zhí)行讀出和寫入操作该贾。


阻塞 與 非阻塞IO

CPU層次:
現(xiàn)代操作系統(tǒng)通常使用異步非阻塞方式進(jìn)行IO羔杨,即發(fā)出IO請(qǐng)求之后,并不等待IO操作完成杨蛋,而是繼續(xù)執(zhí)行下面的指令(非阻塞),IO操作和CPU指令互不干擾(異步)理澎,最后通過中斷的方式來通知IO操作完成結(jié)果逞力。

線程層次:
操作系統(tǒng)為了減輕程序員的思考負(fù)擔(dān),將底層的異步非阻塞的IO方式進(jìn)行封裝糠爬,把相關(guān)系統(tǒng)調(diào)用(如read最疆,write等)以同步的方式展現(xiàn)出來捆探。

而以同步展現(xiàn)的IO又會(huì)帶來新的問題,即為:
同步阻塞的IO會(huì)使線程掛起(后面的指令都等著IO),
同步非阻塞的IO會(huì)消耗CPU資源在輪詢

為解決這一問題逗柴,有新的解決方案
多線程(同步阻塞);
IO多路復(fù)用(select视译,poll院水,epoll)(同步非阻塞,嚴(yán)格地來講屋摔,是把阻塞點(diǎn)改變了位置)烁设;


一、內(nèi)存管理

堆棧是內(nèi)存中的一個(gè)數(shù)據(jù)結(jié)構(gòu)5鍪浴W昂凇副瀑!

1.1、內(nèi)存布局

  • 棧:
    局部變量恋谭,函數(shù)參數(shù)等存儲(chǔ)在該區(qū)糠睡,由編譯器自動(dòng)分配和釋放函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放疚颊。
    棧的內(nèi)存空間是連續(xù)的铜幽,效率很高,但棧的內(nèi)存空間有限串稀。

  • 堆:
    需要程序員手動(dòng)分配和釋放除抛,一個(gè)new就要對(duì)應(yīng)一個(gè)delete。
    如果程序員沒有釋放掉母截,那么在程序結(jié)束后到忽,操作系統(tǒng)會(huì)自動(dòng)回收。
    內(nèi)存空間幾乎沒有限制清寇,內(nèi)存空間不連續(xù)喘漏,因此會(huì)產(chǎn)生內(nèi)存碎片

  • 自由存儲(chǔ)區(qū):
    由malloc等分配的內(nèi)存塊华烟,和堆十分相似的翩迈,不過它是用free來結(jié)束自己的生命的。

  • 全局/靜態(tài)存儲(chǔ)區(qū):
    全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中盔夜,它們共用一塊存儲(chǔ)區(qū)负饲。
    全局變量,靜態(tài)變量分配到該區(qū)喂链,到程序結(jié)束時(shí)自動(dòng)釋放返十。
    包括DATA段(全局初始化區(qū))與BBS段(全局未初始化段):在程序執(zhí)行前BBS段自動(dòng)清零,所以未初始化的全局變量和靜態(tài)變量在程序執(zhí)行前已經(jīng)成為0椭微。

  • 常量存儲(chǔ)區(qū)
    存放的是常量洞坑,不允許修改。

舉例

 int a = 0; //全局初始化區(qū)
 char *p1; //全局未初始化區(qū)
    void main()
   {
        int b; //棧
        char s[] = "abc"; //棧
        char *p2; //棧
        char *p3 = "123456"; //123456{post.content}在常量區(qū)蝇率,p3在棧上
  }

1.2迟杂、堆和棧的區(qū)別

管理方式不同:手動(dòng)——自動(dòng)
空間大小不同:不連續(xù),但幾乎沒有限制——連續(xù)本慕,但是有限
產(chǎn)生碎片不同:

生長(zhǎng)方向不同:對(duì)于堆來說排拷,是向著內(nèi)存地址增加的方向;對(duì)于棧來講间狂,是向著內(nèi)存地址減小的方向增長(zhǎng)攻泼。

分配方式不同:堆都是動(dòng)態(tài)分配的,沒有靜態(tài)分配的堆。
棧的靜態(tài)分配是編譯器完成的忙菠,比如局部變量的分配何鸡。
棧的動(dòng)態(tài)分配由alloca函數(shù)進(jìn)行分配,但是棧的動(dòng)態(tài)分配和堆是不同的牛欢,他的動(dòng)態(tài)分配是由編譯器進(jìn)行釋放骡男,無需我們手工實(shí)現(xiàn)

分配效率不同:
棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu)傍睹,壓棧出棧都有專門的指令執(zhí)行隔盛,這就決定了棧的效率比較高
堆則是C/C++函數(shù)庫(kù)提供的拾稳,它的機(jī)制很復(fù)雜吮炕,庫(kù)函數(shù)會(huì)按照一定的算法在堆內(nèi)存中搜索可用的足夠大小的空間

1.3访得、內(nèi)存分配的問題

  • a.內(nèi)存分配未成功龙亲,卻使用了它 :在使用內(nèi)存之前檢查指針是否為NULL。
    如果指針p是函數(shù)的參數(shù)悍抑,那么在函數(shù)的入口處用assert()
    如果是用malloc或new來申請(qǐng)內(nèi)存鳄炉,應(yīng)該用if(p==NULL) 進(jìn)行防錯(cuò)處理

補(bǔ)充:assert()作用是如果它的條件返回錯(cuò)誤,則終止程序執(zhí)行,一般不使用搜骡。

  • b. 內(nèi)存分配雖然成功拂盯,但是尚未初始化就引用它:創(chuàng)建數(shù)組,別忘了賦初值

  • c. 操作越過了內(nèi)存的邊界:注意下標(biāo)

  • d. 忘記了釋放內(nèi)存:malloc與free的使用次數(shù)一定要相同(new/delete同理)记靡,否則肯定有錯(cuò)誤谈竿。

  • e. 釋放了內(nèi)存卻繼續(xù)使用它:用free或delete釋放了內(nèi)存之后,立即將指針設(shè)置為NULL簸呈,防止產(chǎn)生“野指針”榕订。

1.3、指針與數(shù)組

  • 數(shù)組名對(duì)應(yīng)著(而不是指向)一塊內(nèi)存蜕便,其地址與容量在生命期內(nèi)保持不變,只有數(shù)組的內(nèi)容可以改變贩幻。

  • 指針可以隨時(shí)指向任意類型的內(nèi)存塊轿腺,它的特征是“可變”。

若想把數(shù)組a的內(nèi)容復(fù)制給數(shù)組b丛楚,不能用語句 b = a 族壳,否則將產(chǎn)生編譯錯(cuò)誤。應(yīng)該用標(biāo)準(zhǔn)庫(kù)函數(shù)strcpy進(jìn)行復(fù)制趣些。
比較b和a的內(nèi)容是否相同仿荆,不能用if(b==a) 來判斷,應(yīng)該用標(biāo)準(zhǔn)庫(kù)函數(shù)strcmp進(jìn)行比較


2 多線程

2.1 Linux環(huán)境下的C++多線程編程

什么是臨界區(qū)
臨界區(qū):當(dāng)兩個(gè)線程競(jìng)爭(zhēng)同一資源時(shí)拢操,如果對(duì)資源的訪問順序敏感锦亦,就稱存在競(jìng)態(tài)條件。導(dǎo)致競(jìng)態(tài)條件發(fā)生的代碼區(qū)稱作臨界區(qū)令境。

2.1.1 noncopyable

同意程序?qū)崿F(xiàn)一個(gè)不可復(fù)制類杠园。
定義一個(gè)類時(shí) C++會(huì)默認(rèn)生成 復(fù)制構(gòu)造函數(shù)復(fù)制賦值操作符

class empty_class{
public:
    empty_class(const empty_class &){...}
    empty_class & operator=(const empty_class &){...}
};

原因:有些對(duì)象是獨(dú)一無二的舔庶,作備份不合邏輯抛蚁。

2.1.2 pthread_mutex_t

linux線程互斥量pthread_mutex_t,當(dāng)另一個(gè)線程也要訪問這個(gè)變量時(shí)惕橙,發(fā)現(xiàn)這個(gè)變量被鎖住了瞧甩,無法訪問,它就會(huì)一直等待弥鹦。

2.1.2 pid_t

pid_t是一個(gè)typedef定義類型肚逸,實(shí)際上就是一個(gè)int類型。用它來表示進(jìn)程id類型惶凝。

2.1.3 nullptr與NULL

在C++中吼虎,一個(gè)空指針要么是一個(gè)字面值整形,要么是一個(gè)std::nullptr_t苍鲜。
C++中的NULL思灰,其實(shí)就是一個(gè)0,這會(huì)導(dǎo)致很多問題混滔,func(NULL)會(huì)去調(diào)用void func(int)洒疚,所以引入nullptr

2.1.4 boost::bind

TODO:

2.1.5 pthread_cond_t

pthread_cond_t表示多線程的條件變量坯屿,用于控制線程等待和就緒的條件油湖。
pthread_cond_destroy用于銷毀一個(gè)條件變量

2.1.6 __thread

__thread 關(guān)鍵字表示每一個(gè)線程都有一份獨(dú)立的實(shí)體,且互不干擾领跛。
只能修飾:基本變量乏德、指針變量、不帶自定義構(gòu)造函數(shù)和析構(gòu)函數(shù)的類吠昭。

2.1.7 extern

a.C++ primer 4 -- 669 指定使用其他語言編寫的函數(shù)

b.如果想聲明一個(gè)變量而非定義它喊括,就在變量名前添加extern關(guān)鍵字

2.1.8 timespec

struct timespec 和 struct timeval 是Linux環(huán)境下的兩個(gè)時(shí)間struct,主要用于由函數(shù)int clock_gettime(clockid_t, struct timespec *)獲取特定時(shí)鐘的時(shí)間矢棚,其中的clockid_t用以下幾種時(shí)鐘:

  • CLOCK_REALTIME 統(tǒng)當(dāng)前時(shí)間郑什,從1970年1.1日算起
  • CLOCK_MONOTONIC 系統(tǒng)的啟動(dòng)時(shí)間,不能被設(shè)置
  • CLOCK_PROCESS_CPUTIME_ID 本進(jìn)程運(yùn)行時(shí)間
  • CLOCK_THREAD_CPUTIME_ID 本線程運(yùn)行時(shí)間

timespec有兩個(gè)成員蒲肋,一個(gè)是秒蘑拯,一個(gè)是納秒, 所以最高精確度是納秒钝满。

2.1.9 跨平臺(tái)的數(shù)據(jù)格式

數(shù)據(jù)類型特別是int相關(guān)的類型在不同位數(shù)機(jī)器的平臺(tái)下長(zhǎng)度不同,為了保證平臺(tái)的通用性申窘,程序中盡量不要使用long數(shù)據(jù)庫(kù)型 弯蚜。
可以用int64_t來表示C++中的long long

2.1.10 static_cast

static_cast是一個(gè)強(qiáng)制類型轉(zhuǎn)換操作符。強(qiáng)制類型轉(zhuǎn)換偶洋,也稱為顯式轉(zhuǎn)換熟吏。

double a = 1.999;
int b = static_cast<double>(a); 

使用static_cast可以找回存放在void*指針中的值。

    double a = 1.999;
    void * vptr = & a;
    double * dptr = static_cast<double*>(vptr);

static_cast也可以用在于基類與派生類指針或引用類型之間的轉(zhuǎn)換玄窝。然而它不做運(yùn)行時(shí)的檢查牵寺,不如dynamic_cast安全。

2.1.11 c++ 時(shí)間類型

Unix時(shí)間戳是一種時(shí)間表示方式恩脂,定義為從格林威治時(shí)間1970年01月01日00時(shí)00分00秒起至現(xiàn)在的總秒數(shù)帽氓。
time_t 這種類型(struct)就是用來存儲(chǔ)從1970年到現(xiàn)在經(jīng)過了多少秒。

2.1.13 靜態(tài)斷言

static_assert(常量表達(dá)式俩块,提示字符串)
如果第一個(gè)參數(shù)常量表達(dá)式的值為真(true或者非零值)黎休,那么static_assert不做任何事情,就像它不存在一樣玉凯,否則會(huì)產(chǎn)生一條編譯錯(cuò)誤势腮,錯(cuò)誤位置就是該static_assert語句所在行,錯(cuò)誤提示就是第二個(gè)參數(shù)提示字符串漫仆。

2.1.14 std::is_same

std::is_same 判斷類型是否一致捎拯,兩個(gè)一樣的類型會(huì)返回true。

2.1.15 棧追蹤

獲取當(dāng)前線程的調(diào)用堆棧,獲取的信息將會(huì)被存放在buffer中
int backtrace(void **buffer,int size)盲厌,參數(shù) size 用來指定buffer中可以保存多少個(gè) void*元素

char ** backtrace_symbols (void *const *buffer, int size)將從backtrace函數(shù)獲取的信息轉(zhuǎn)化為一個(gè)字符串?dāng)?shù)組署照,size是該數(shù)組中的元素個(gè)數(shù)

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

大多數(shù)機(jī)器上,調(diào)用函數(shù)要做很多工作吗浩,調(diào)用前要先保存寄存器建芙,并且在返回時(shí)恢復(fù)。程序還必須轉(zhuǎn)向一個(gè)新的位置懂扼。
內(nèi)聯(lián)函數(shù)保證他在每一個(gè)調(diào)用節(jié)點(diǎn)上禁荸,內(nèi)聯(lián)地展開,適用于小但是調(diào)用很頻繁的函數(shù)

2.2 強(qiáng)引用與弱引用

2.2.1 weak_ptr

weak_ptr 是一種不控制對(duì)象生命周期的智能指針, 它指向一個(gè) shared_ptr 管理的對(duì)象.
不會(huì)改變shared_ptr的引用計(jì)數(shù)阀湿。不論是否有weak_ptr指向屡限,一旦最后一個(gè)指向?qū)ο蟮膕hared_ptr被銷毀,對(duì)象就會(huì)被釋放炕倘。

a.升級(jí)
如果對(duì)象存在,weak_ptr 的 lock()函數(shù)返回一個(gè)指向共享對(duì)象的shared_ptr翰撑,此時(shí)如果原來的shared_ptr被銷毀罩旋,則該對(duì)象的生命期將被延長(zhǎng)至這個(gè)臨時(shí)的shared_ptr同樣被銷毀為止啊央。
如果weak_ptr指向的對(duì)象被銷毀,否則返回一個(gè)空shared_ptr涨醋。

b.打破循環(huán)引用

class ClassA
{
public:
    ClassA() {}
    ~ClassA() {}

private:
    weak_ptr<ClassB> pb;  // 在A中引用B
};

class ClassB
{
public:
    ClassB() {}
    ~ClassB() {}

private:
    weak_ptr<ClassA> pa;  // 在B中引用A
};

int main() {
    shared_ptr<ClassA> spa = make_shared<ClassA>();
    shared_ptr<ClassB> spb = make_shared<ClassB>();
    // 因?yàn)闆]改變shared_ptr的引用計(jì)數(shù)瓜饥,此時(shí)引用計(jì)數(shù)為1,超過作用域后自動(dòng)釋放
}

因?yàn)闆]改變shared_ptr的引用計(jì)數(shù)浴骂,此時(shí)引用計(jì)數(shù)為1乓土,超過作用域后自動(dòng)釋放。

2.2.2 enable_shared_from this
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);

此時(shí)這兩個(gè)shared_ptr指針溯警,所以當(dāng)其中一個(gè)在析構(gòu)時(shí)有可能資源已經(jīng)被釋放了趣苏。
Similarly, if a member function needs a shared_ptr object that owns the object that it's being called on, it can't just create an object on the fly:

struct S : enable_shared_from_this<S>
{
  shared_ptr<S> not_dangerous()
  {
    return shared_from_this();
  }
};

When you do this, keep in mind that the object on which you call shared_from_this must be owned by a shared_ptr object.

2.2.3 臨界區(qū)

臨界區(qū)指的是一個(gè)訪問共用資源的程序片段

2.2.4 copy on write——COW

有多個(gè)調(diào)用者使用相同的資源時(shí),他們會(huì)共同獲取相同的指針指向相同的資源梯轻。內(nèi)核此時(shí)并不復(fù)制食磕。
直到某個(gè)調(diào)用者試圖修改資源的內(nèi)容時(shí),內(nèi)核才會(huì)真正復(fù)制一份專用副本(private copy)給該調(diào)用者喳挑,而其他調(diào)用者所見到的最初的資源仍然保持不變彬伦。
這是一種種延時(shí)懶惰策略。

2.2.5 unique_ptr

unique_ptr 獨(dú)占所指向的對(duì)象, 同一時(shí)刻只能有一個(gè) unique_ptr 指向給定對(duì)象伊诵,在 unique_ptr 離開作用域時(shí)釋放該對(duì)象单绑。

在下列兩者之一發(fā)生時(shí)用關(guān)聯(lián)的刪除器釋放對(duì)象:

  • 銷毀了管理的 unique_ptr 對(duì)象
  • 通過 operator=reset() 賦值另一指針給管理的 unique_ptr 對(duì)象。

shared_ptr的unique函數(shù):檢查所管理對(duì)象是否僅由當(dāng)前 shared_ptr 的實(shí)例管理

2.2.5 std::const_iterator

const_iterator 可遍歷曹宴,不可改變所指元素

const iterator 不可遍歷搂橙,可改變所指元素:iterator本身里面存的是指針,指針不能改變浙炼,也就是不能指向其他的位置

2.2.6 shared_ptr 的線程安全性

同一個(gè)shared_ptr對(duì)象可以被多線程同時(shí)讀取份氧。
不同的shared_ptr對(duì)象可以被多線程同時(shí)修改(即使這些shared_ptr對(duì)象管理著同一個(gè)對(duì)象的指針)

2.2.7 size_t

與機(jī)器相關(guān)的unsigned類型,其大小足以保證存儲(chǔ)內(nèi)存中對(duì)象的大小弯屈。
size_t在32位架構(gòu)上是4字節(jié)蜗帜,在64位架構(gòu)上是8字節(jié).

2.2.8 implicit_cast
struct top {};  //最頂層的父類
struct mid_a : top {}; 
struct mid_b : top {};
struct bottom : mid_a, mid_b {};  //最底層的派生類

之前的static_cast太強(qiáng)大了, 強(qiáng)大到可以進(jìn)行”down-cast”. 于是編譯器沒有任何的警告, 就可以把一個(gè)top類型的引用給強(qiáng)制轉(zhuǎn)換成了min_a的引用.

在C++世界的英文里, 我們說”convert”通常指”implicit convert”, 而”cast”指explicit cast(顯式)

implicit cast——隱式cast

2.3 兩個(gè)類互相引用

根本原因:定義A的時(shí)候,A的里面有B资厉,所以就需要去查看B的占空間大小厅缺,但是查看的時(shí)候又發(fā)現(xiàn)需要知道A的占空間大小,造成死循環(huán)宴偿。

2.3.1 解決方案
#include <MutexLock.h>
#include <set>

class Request;

class Inventory {
public:
    void add(Request *request) {
       // 
    };
private:
    mutable MutexLock mutexLock;
    std::set<Request *> requests;
};

在Inventory.h中湘捎,采用的class Request的前置聲明,但是在class Inventory的聲明中只能定義Class Request的指針或引用窄刘。

原理:雖然在B的定義文件中并沒有導(dǎo)入A的頭文件窥妇,不知道A的占空間大小,但是由于在B中調(diào)用A的時(shí)候用的指針形式娩践,B只知道指針占4個(gè)字節(jié)就可以活翩,不需要知道A真正占空間大小烹骨。

2.3.2 前置聲明
2.3.3 字節(jié)的占用
2.3.4 C++類的this
2.3.4 boost::function()
2.3.5 mangle和demangle

ABI是Application Binary Interface的簡(jiǎn)稱。 C/C++發(fā)展的過程中材泄,二進(jìn)制兼容一直是個(gè)問題沮焕。不同編譯器廠商編譯的二進(jìn)制代碼之間兼容性不好,甚至同一個(gè)編譯器的不同版本之間兼容性也不好拉宗。
C/C++語言在編譯以后峦树,函數(shù)的名字會(huì)被編譯器修改,改成編譯器內(nèi)部的名字旦事,這個(gè)名字會(huì)在鏈接的時(shí)候用到魁巩。
識(shí)別C++編譯以后的函數(shù)名的過程,就叫demangle族檬。

應(yīng)用:打印異常日志

2.3.6 volatile

在匯編層面反映出來歪赢,就是兩條語句,下一條語句不會(huì)直接使用上一條語句對(duì)應(yīng)的volatile變量的寄存器內(nèi)容单料,而是重新從內(nèi)存中讀取埋凯。

2.3.10 RTTI

2.X 之補(bǔ)充 — 移動(dòng)右值引用 ( C++11特性 )

2.X.1右值引用
  • a.判斷左值與右值
    典型情況下左值和右值可以通過在賦值表達(dá)式中的位置進(jìn)行判斷,在等號(hào)左邊的為左值扫尖、等號(hào)右邊的為右值
    另外一個(gè)判別方法是:可以取地址白对、有名字的就是左值,否則就是右值换怖。

  • b.左值與右值的區(qū)別
    左值和右值都是針對(duì)表達(dá)式而言的甩恼,左值是指表達(dá)式結(jié)束后依然存在的持久對(duì)象,右值是指表達(dá)式結(jié)束時(shí)就不再存在的臨時(shí)對(duì)象
    int b = 20; //這里b是左值 20是右值 ,因?yàn)檫@個(gè)表達(dá)式過后 20將不存在了 而b依然存在

  • c.右值引用的初始化
    右值引用沉颂,是對(duì)臨時(shí)對(duì)象的一種引用条摸,它是在初始化時(shí)完成引用的,右值引用可以在初始化后改變臨時(shí)對(duì)象的值
    int &&i = 1; i綁定到了右值1

2.X.2 移動(dòng)MOVE
int i = 1;
int&& rr = std::move(i);

移動(dòng)操作竊取了對(duì)象資源的控制權(quán)铸屉,從而避免了不必要的拷貝钉蒲。

2.X.3 四行代碼的故事

第一行: int i = getVar();
這行代碼會(huì)產(chǎn)生兩種類型的值,一種是左值i彻坛,一種是函數(shù)getVar()返回的臨時(shí)值顷啼,這個(gè)臨時(shí)值在表達(dá)式結(jié)束后就銷毀了,而左值i在表達(dá)式結(jié)束后仍然存在昌屉,這個(gè)臨時(shí)值就是右值

第二行: T&& k = getVar();
getVar()產(chǎn)生的臨時(shí)值不會(huì)像第一行代碼那樣钙蒙,在表達(dá)式結(jié)束之后就銷毀了,而是會(huì)被“續(xù)命”间驮,他的生命周期將會(huì)通過右值引用得以延續(xù)躬厌,和變量k的聲明周期一樣長(zhǎng)

第三行: T(T&& a) : m_val(val){ a.m_val=nullptr; }
移動(dòng)構(gòu)造函數(shù)

class A
{
public:
    A() :m_ptr(new int(0)){}
    A(const A& a):m_ptr(new int(*a.m_ptr)) //深拷貝的拷貝構(gòu)造函數(shù)
    {
        cout << "copy construct" << endl;
    }
    A(A&& a) :m_ptr(a.m_ptr)
    {
        a.m_ptr = nullptr;
        cout << "move construct" << endl;
    }
    ~A(){ delete m_ptr;}
private:
    int* m_ptr;
};
int main(){
    A a = Get(false); 
} 
輸出:
construct
move construct
move construct

輸出結(jié)果表明竞帽,并沒有調(diào)用拷貝構(gòu)造函數(shù)烤咧,只調(diào)用了移動(dòng)構(gòu)造函數(shù)偏陪,它的參數(shù)是一個(gè)右值引用類型。

std::list< std::string> tokens;//省略初始化...
std::list< std::string> t = tokens; //這里存在拷貝 

std::list< std::string> tokens;
std::list< std::string> t = std::move(tokens);  //這里沒有拷貝

使用move幾乎沒有任何代價(jià)煮嫌,只是轉(zhuǎn)換了資源的所有權(quán)。他實(shí)際上將左值變成右值引用抱虐,然后應(yīng)用移動(dòng)語義昌阿,調(diào)用移動(dòng)構(gòu)造函數(shù),就避免了拷貝恳邀,提高了程序性能懦冰。如果一個(gè)對(duì)象內(nèi)部有較大的堆內(nèi)存或者動(dòng)態(tài)數(shù)組時(shí),很有必要寫move語義的拷貝構(gòu)造函數(shù)和賦值函數(shù)谣沸,避免無謂的深拷貝刷钢,以提高性能。

第四行:

void processValue(int& a){ cout << "lvalue" << endl; }
void processValue(int&& a){ cout << "rvalue" << endl; }
template <typename T>
void forwardValue(T&& val)
{
    processValue(std::forward<T>(val)); //照參數(shù)本來的類型進(jìn)行轉(zhuǎn)發(fā)乳附。
}
void Testdelcl()
{
    int i = 0;
    forwardValue(i); //傳入左值 
    forwardValue(0);//傳入右值 
}
輸出:
lvaue 
rvalue

按照參數(shù)的實(shí)際類型進(jìn)行轉(zhuǎn)發(fā)内地。

2.X.4 深拷貝與淺拷貝

淺拷貝是增加了一個(gè)指針,指向原來已經(jīng)存在的內(nèi)存赋除。
PS:要注意淺拷貝的指針被析構(gòu)兩次阱缓。

而深拷貝是增加了一個(gè)指針,并新開辟了一塊空間举农。

2.4 Reactor模式——一種高性能IO

2.4.1 原始的網(wǎng)絡(luò)編程思想

while(true){
socket = accept();
new thread(socket);
}
while循環(huán)不斷監(jiān)聽端口是否有新的套接字連接荆针,配合多線程,每一個(gè)連接用一個(gè)線程處理颁糟。
缺點(diǎn):如果連接數(shù)太高航背,系統(tǒng)無法承受,如果使用線程池棱貌,會(huì)導(dǎo)致線程的粒度太大玖媚。每一個(gè)線程把一次交互的事情全部做了。

2.4.2 基于事件驅(qū)動(dòng)

應(yīng)該把一次連接的操作分為更細(xì)的粒度键畴,這些更細(xì)的粒度是更小的線程最盅。整個(gè)線程池的數(shù)目會(huì)翻倍,但是線程更簡(jiǎn)單起惕,任務(wù)更加單一涡贱。
這其實(shí)就是Reactor出現(xiàn)的原因,在Reactor中惹想,這些被拆分的小線程或者子過程對(duì)應(yīng)的是handler问词,每一種handler會(huì)出處理一種event

2.5 無鎖化編程

2.5.1 RAM與ROM

RAM:隨機(jī)存取存儲(chǔ)器random access memory嘀粱,是與CPU直接交換數(shù)據(jù)的內(nèi)部存儲(chǔ)器激挪,也叫內(nèi)存辰狡。它可以隨時(shí)讀寫,而且速度很快垄分。當(dāng)電源關(guān)閉時(shí)RAM不能保留數(shù)據(jù)宛篇。
ROM:ROM是Read Only Memory的縮寫

2.5.2 原子操作

鎖的缺點(diǎn):lock鎖的是FSB,前端串行總線薄湿,這個(gè)FSB是處理器CPU和RAM之間的總線叫倍,鎖住FSB,就能阻止其他處理器從RAM獲取數(shù)據(jù)豺瘤。當(dāng)然這種操作開銷相當(dāng)大吆倦,只能操作小的內(nèi)存可以這樣做,想想有memcpy坐求,如果操作一大片內(nèi)存蚕泽,鎖內(nèi)存,那么代價(jià)太大了桥嗤。

原子操作:所謂原子操作是指不會(huì)被線程調(diào)度機(jī)制打斷的操作须妻;這種操作一旦開始,就一直運(yùn)行到結(jié)束砸逊,中間不會(huì)有任何 context switch (切換到另一個(gè)線程)璧南。

_sync_fetch_and_add,先fetch师逸,然后自加司倚,返回的是自加以前的值。
以count = 4為例篓像,調(diào)用__sync_fetch_and_add(&count,1)之后动知,返回值是4,然后员辩,count變成了5.
__sync_bool_compare_and_swap

三盒粮、IO多路復(fù)用

struct pollfd {
    int fd;         /* 文件描述符 */
    short events;   /* 等待的事件 */
    short revents;  /* 實(shí)際發(fā)生了的事件 */
} ;

后面的fd均指文件描述符

3.1、流

不管是文件奠滑,還是套接字丹皱,還是管道,我們都可以把他們看作流宋税。

3.2摊崭、阻塞與非阻塞 (線程層次)

可以簡(jiǎn)單理解為調(diào)用一個(gè)IO操作能不能立即得到返回應(yīng)答,如果不能立即獲得返回杰赛,需要等待呢簸,那就阻塞了

3.3、同步與異步

同步IO操作將導(dǎo)致請(qǐng)求的進(jìn)程一直被blocked,直到IO操作完成根时。從這個(gè)層次來瘦赫,阻塞IO、非阻塞IO操作蛤迎、IO多路復(fù)用都是同步IO确虱。

3.4、多路復(fù)用IO

阻塞I/O有一個(gè)比較明顯的缺點(diǎn)是在I/O阻塞模式下忘苛,一個(gè)線程只能處理一個(gè)流的I/O事件蝉娜。

多路復(fù)用IO也是阻塞IO,只是阻塞的方法是select/poll/epoll扎唾。select/epoll的好處就在于單個(gè)process就可以同時(shí)處理多個(gè)網(wǎng)絡(luò)連接的IO。它的基本原理是select/epoll這個(gè)函數(shù)會(huì)不斷輪詢所負(fù)責(zé)的IO操作南缓,當(dāng)某個(gè)IO操作有數(shù)據(jù)到達(dá)時(shí)胸遇,就通知用戶進(jìn)程。然后由用戶進(jìn)程去操作IO汉形。

3.6纸镊、Linux的用戶空間(User space)和內(nèi)核空間( Kernel space)

x86 CPU采用了段頁式地址映射模型。進(jìn)程代碼中的地址為邏輯地址概疆,經(jīng)過段頁式地址映射后逗威,才真正訪問物理內(nèi)存


Linux 操作系統(tǒng)和驅(qū)動(dòng)程序運(yùn)行在內(nèi)核空間岔冀,應(yīng)用程序運(yùn)行在用戶空間凯旭,兩者不能簡(jiǎn)單地使用指針傳遞數(shù)據(jù)。

3.6使套、select

select本質(zhì)上是通過設(shè)置或者檢查存放fd標(biāo)志位的數(shù)據(jù)結(jié)構(gòu)來進(jìn)行下一步處理罐呼。

僅僅知道有I/O事件發(fā)生了,卻并不知道是哪那幾個(gè)流(可能有一個(gè)侦高,多個(gè)嫉柴,甚至全部),只能無差別輪詢所有流奉呛,找出能讀出數(shù)據(jù)计螺,或者寫入數(shù)據(jù)的流,對(duì)他們進(jìn)行操作瞧壮。
1024- 32 || 2048 -64

3.7登馒、poll

poll本質(zhì)上和select沒有區(qū)別,它將用戶傳入的數(shù)組拷貝到內(nèi)核空間馁痴,然后查詢每個(gè)fd對(duì)應(yīng)的設(shè)備狀態(tài)谊娇,如果設(shè)備就緒則在設(shè)備等待隊(duì)列中加入一項(xiàng)并繼續(xù)遍歷。
如果遍歷完所有fd后沒有發(fā)現(xiàn)就緒設(shè)備,則掛起當(dāng)前進(jìn)程济欢,直到設(shè)備就緒或者主動(dòng)超時(shí)赠堵,被喚醒后它又要再次遍歷fd。

它沒有最大連接數(shù)的限制法褥,原因是它是基于鏈表來存儲(chǔ)的

3.8茫叭、epoll

Epoll最大的優(yōu)點(diǎn)就在于它只管“活躍”的連接,而跟連接總數(shù)無關(guān)半等。

3.9揍愁、

3.9.1、__builtin_expect

指令的寫法為:__builtin_expect(EXP, N)杀饵,意思是:EXP==N的概率很大莽囤。
__builtin_expect() 是 GCC (version >= 2.96)提供給程序員使用的,目的是將“分支轉(zhuǎn)移”的信息提供給編譯器切距,這樣編譯器可以對(duì)代碼進(jìn)行優(yōu)化朽缎,以減少指令跳轉(zhuǎn)帶來的性能下降
例子:GCC編譯的指令會(huì)優(yōu)先讀取 y = -1

int x, y;
 if(unlikely(x > 0))
    y = 1; 
else 
    y = -1;
3.9.1谜悟、GCC

GCC(GNU Compiler Collection话肖,GNU編譯器套件),是由 GNU 開發(fā)的編程語言編譯器葡幸。

四最筒、網(wǎng)絡(luò)編程

4.1 UNIX環(huán)境高級(jí)編程

4.1.1 readv和writev函數(shù)
#include<sys/uio.h>
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
//若成功則返回已讀,寫的字節(jié)數(shù)蔚叨,若出錯(cuò)則返回-1床蜘。
4.1.2 C++中的字節(jié)序

字節(jié)序的含義:大于一個(gè)字節(jié)類型的數(shù)據(jù)在內(nèi)存中的存放順序。比如short 或者int在不同的字節(jié)序存儲(chǔ)結(jié)果是不一樣的缅叠。
大字節(jié)序(Big-Endian):高位字節(jié)排放在內(nèi)存的低地址端悄泥,低位字節(jié)排放在內(nèi)存的高地址端
小字節(jié)序(Little-Endian):低位字節(jié)排放在內(nèi)存的低地址端,高位字節(jié)排放在內(nèi)存的高地址端肤粱。

4.1.2 char 與 uint8_t

一個(gè)是字符類型弹囚,一個(gè)是超短無符號(hào)整型,他們唯一一樣的地方就是占內(nèi)存大小一樣领曼。

序列化:將對(duì)象或數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)為字節(jié)序列的過程鸥鹉。

4.1.3 reinterpret_cast

Reinterpret_cast:編譯器不會(huì)做任何檢查,截?cái)嗍荆a(bǔ)齊的操作毁渗,只是把比特位拷貝過去.
所以 reinterpret_cast 常常被用作不同類型指針間的相互轉(zhuǎn)換,因?yàn)樗蓄愋偷闹羔樀拈L(zhǎng)度都是一致的(32位系統(tǒng)上都是4字節(jié))单刁,按比特位拷貝后不會(huì)損失數(shù)據(jù).

2.6 event loop

所有同步任務(wù)都在主線程上執(zhí)行
異步任務(wù)指的是灸异,不進(jìn)入主線程、而進(jìn)入任務(wù)隊(duì)列的任務(wù),只有任務(wù)隊(duì)列通知主線程肺樟,某個(gè)異步任務(wù)可以執(zhí)行了檐春,該任務(wù)才會(huì)進(jìn)入主線程執(zhí)行。
主線程從"任務(wù)隊(duì)列"中讀取事件么伯,這個(gè)過程是循環(huán)不斷的疟暖。

為什么不能用if來等待條件變量?spurious wakeup

event loop

計(jì)算機(jī)的內(nèi)存回收算法
HTTP中的GET和POST
C++ 頭文件引用的庫(kù)在cpp文件里可以引用嗎

new創(chuàng)建對(duì)象直接使用堆空間田柔,而局部不用new定義類對(duì)象則使用椑停空間

important C++的編譯問題

tcpdump WireShark

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市硬爆,隨后出現(xiàn)的幾起案子欣舵,更是在濱河造成了極大的恐慌,老刑警劉巖缀磕,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邻遏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡虐骑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門赎线,熙熙樓的掌柜王于貴愁眉苦臉地迎上來廷没,“玉大人,你說我怎么就攤上這事垂寥〉呃瑁” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵滞项,是天一觀的道長(zhǎng)狭归。 經(jīng)常有香客問我,道長(zhǎng)文判,這世上最難降的妖魔是什么过椎? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮戏仓,結(jié)果婚禮上疚宇,老公的妹妹穿的比我還像新娘。我一直安慰自己赏殃,他們只是感情好敷待,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著仁热,像睡著了一般榜揖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天举哟,我揣著相機(jī)與錄音思劳,去河邊找鬼。 笑死炎滞,一個(gè)胖子當(dāng)著我的面吹牛敢艰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播册赛,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼钠导,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了森瘪?” 一聲冷哼從身側(cè)響起牡属,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扼睬,沒想到半個(gè)月后逮栅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窗宇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年措伐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片军俊。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侥加,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出粪躬,到底是詐尸還是另有隱情担败,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布镰官,位于F島的核電站提前,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏泳唠。R本人自食惡果不足惜狈网,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望警检。 院中可真熱鬧孙援,春花似錦、人聲如沸扇雕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽镶奉。三九已至础淤,卻和暖如春崭放,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸽凶。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工币砂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玻侥。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓决摧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親凑兰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掌桩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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

  • 1.C和C++的區(qū)別?C++的特性姑食?面向?qū)ο缶幊痰暮锰帲?答:c++在c的基礎(chǔ)上增添類波岛,C是一個(gè)結(jié)構(gòu)化語言,它的重...
    杰倫哎呦哎呦閱讀 9,496評(píng)論 0 45
  • 從三月份找實(shí)習(xí)到現(xiàn)在音半,面了一些公司则拷,掛了不少,但最終還是拿到小米曹鸠、百度煌茬、阿里、京東彻桃、新浪宣旱、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,218評(píng)論 11 349
  • 1. 基礎(chǔ)知識(shí) 1.1叛薯、 基本概念、 功能 馮諾伊曼體系結(jié)構(gòu)1笙纤、計(jì)算機(jī)處理的數(shù)據(jù)和指令一律用二進(jìn)制數(shù)表示2耗溜、順序執(zhí)...
    yunpiao閱讀 5,274評(píng)論 1 22
  • 1. C++基礎(chǔ)知識(shí)點(diǎn) 1.1 有符號(hào)類型和無符號(hào)類型 當(dāng)我們賦給無符號(hào)類型一個(gè)超出它表示范圍的值時(shí),結(jié)果是初始值...
    Mr希靈閱讀 17,969評(píng)論 3 82
  • 搬運(yùn)自攀∪荩客網(wǎng)大神總結(jié) extern關(guān)鍵字 extern修飾變量是個(gè)聲明抖拴,此變量/函數(shù)是在別處定義的,要在此處引用 ...
    leon4ever閱讀 3,654評(píng)論 0 9