亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解C++之C++11的牛逼特性

 更新時(shí)間:2020年09月10日 11:45:42   作者:愛(ài)心天使的守護(hù)  
這篇文章主要介紹了C++之C++11的牛逼特性,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

一、列表初始化

1.1 C++98中,標(biāo)準(zhǔn)允許使用花括號(hào){}對(duì)數(shù)組元素進(jìn)行統(tǒng)一的列表初始值設(shè)定。

int array1[] = {1,2,3,4,5};
int array2[] = {0};

對(duì)對(duì)于一些自定義類(lèi)型,卻不行.

vector<int> v{1,2,3,4,5};

在C++98中這樣無(wú)法通過(guò)編譯,因此需要定義vector之后,在使用循環(huán)進(jìn)行初始賦值。
C++11擴(kuò)大了用初始化列表的使用范圍,讓其適用于所有的內(nèi)置類(lèi)型和自定義類(lèi)型,而且使用時(shí),=可以不寫(xiě)

// 內(nèi)置類(lèi)型
int x1 = {10};
int x2{10}
// 數(shù)組
int arr1[5] {1,2,3,4,5}
int arr2[]{1,2,3,4,5};
// 標(biāo)準(zhǔn)容器
vector<int> v{1,2,3}
map<int,int> m{{1,1},{2,2}}
// 自定義類(lèi)型
class Point
{
int x;
int y;
}
Power p{1,2};

1.2 多個(gè)對(duì)象的列表初始化
給類(lèi)(模板類(lèi))添加一個(gè)帶有initializer_list類(lèi)型參數(shù)的構(gòu)造函數(shù)即可支持多個(gè)對(duì)象的,列表初始化.

#include<initializer_list>
template<class T>
class Vector{
public:
	Vecto(initializer_list<T> l)
		:_capacity(l.size())
		,_size(0){
			_array = new T[_capacity];
			for(auto e : l)
				_array[_size++] = 3;
		}
private;
	T* _array;
	size_t _capacity;
	size_t _size;
};

二、變量類(lèi)型推導(dǎo)

2.1 auto
在C++中,可以使用auto來(lái)根據(jù)變量初始化表達(dá)式類(lèi)型推導(dǎo)變量的實(shí)際類(lèi)型,簡(jiǎn)化程序的書(shū)寫(xiě)

// 不使用auto需要寫(xiě)很長(zhǎng)的迭代器的類(lèi)型
map<string,string> m;
map<string,string>::iterator it1 = m.begin();
// 使用auto就很簡(jiǎn)單
auto it2 = m.begin();

2.1 decltype 類(lèi)型推導(dǎo)
auto使用時(shí),必須對(duì)auto聲明的類(lèi)型進(jìn)行初始化,否則編譯器不能推導(dǎo)出auto的實(shí)際類(lèi)型。
但是有些場(chǎng)景可能需要根據(jù)表達(dá)式運(yùn)行后的結(jié)果進(jìn)行類(lèi)型推導(dǎo)。因?yàn)榫幾g時(shí),代碼不會(huì)運(yùn)行,auto也就…

template<class T1,class T2>
T1 Add(const T1& a,const T2& b){
	return a + b;
}

如果用加完后的結(jié)果作為函數(shù)的返回類(lèi)型,可能會(huì)出錯(cuò),這就需要程序運(yùn)行完后才能知道結(jié)果的實(shí)際類(lèi)型,即RTTI(運(yùn)行時(shí)類(lèi)型識(shí)別)
decltype可以根據(jù)表達(dá)式的實(shí)際類(lèi)型推演出定義變量時(shí)所用的類(lèi)型

// 推演表達(dá)式作為變量的定義類(lèi)型
int a = 1,b=2;
decltype(a+b) c;
cout<<typeid(c).name()<<endl;

// 推演函數(shù)的返回值類(lèi)型
void GetMemory(size_t size){
	return malloc(size);
}
cout<<typeid(decltype(GetMemory)).name()<<endl;

三、基于范圍for的循環(huán)

vector<int> v{1,2,3,4,5};
for(const auto& e : v)
	cout<<e<<' ';
cout<<endl;

四、final和override
在我的多態(tài)的文章中有介紹:http://chabaoo.cn/article/162078.htm

五、委派構(gòu)造函數(shù)
委派構(gòu)造函數(shù)可以通過(guò)委派其它構(gòu)造函數(shù),使多構(gòu)造函數(shù)的類(lèi)編寫(xiě)更加容易

class Info
{
public;
	Info()
		:_type(0)
		,_name('s')
		{}
	Info(int type)
		:_type(type)
		,_name('a')
		{}
	Info(char a)
		:_type(0)
		,_name(a)
		{}
pirvate;
	int _type;
	char _name;
};

上面的構(gòu)造函數(shù)除了初始化列表不同之外,其它部分都是類(lèi)似的,代碼重復(fù),可以使用委派構(gòu)造函數(shù)
委派構(gòu)造函數(shù)就是把構(gòu)造的任務(wù)委派給目標(biāo)構(gòu)造函數(shù)來(lái)完成類(lèi)的構(gòu)造

class Info
{
// 目標(biāo)構(gòu)造函數(shù)
public:
	Info()
		:_type(0)
		,_a('a')
	{}
// 委派構(gòu)造函數(shù)
	Info(int type)
		:Info()
		{
			_type = type;
		}
private;
	int _type = 0;
	char _a = 'a';
};

在初始化列表中調(diào)用“基準(zhǔn)版本”的構(gòu)造函數(shù)稱(chēng)為委派構(gòu)造函數(shù),而被調(diào)用的“基準(zhǔn)版本”則稱(chēng)為目標(biāo)構(gòu)造函數(shù)

六、默認(rèn)函數(shù)控制
在C++中對(duì)于空類(lèi),編譯器會(huì)生成一些默認(rèn)的成員函數(shù),如果在類(lèi)中顯式定義了,編譯器就不會(huì)重新生成默認(rèn)版本。但是如果在一個(gè)類(lèi)中聲明了帶參的構(gòu)造函數(shù),如果又需要定義不帶參的實(shí)例化無(wú)參的對(duì)象。這時(shí)候編譯器是有時(shí)生成,有時(shí)不生成,就會(huì)造成混亂,C++11可以讓程序員自己控制是否需要編譯器生成。

6.1 顯式缺省函數(shù)
在C++11中,可以在默認(rèn)函數(shù)定義或聲明時(shí)加上=default,來(lái)讓編譯器生成該函數(shù)的默認(rèn)版本。

class A
{
public:
	A(int a)
		:_a(a)
	{}
	A() = default; // 顯式缺省構(gòu)造函數(shù)
	A& operator=(const A& a); // 在類(lèi)中聲明,在類(lèi)外定義時(shí),讓編譯器生成默認(rèn)賦值運(yùn)算符重載
private:
	int _a;
};
A& A::operator=(const A& a) = default;

6.2 刪除默認(rèn)函數(shù)
要想限制一些默認(rèn)函數(shù)的生成,在C++98中,可以把該函數(shù)設(shè)為私有,不定義,這樣,如果有人調(diào)用就會(huì)報(bào)錯(cuò)。在C++11中,可以給該函數(shù)聲明加上=delete就可以。

class A
{
A(int a)
	:_a(a)
{}
A(constA&) = delete; // 禁止編譯器生成默認(rèn)的拷貝構(gòu)造函數(shù)
private:
	int _a;
};

七、右值引用
7.1 移動(dòng)語(yǔ)義

class String
{
public:
	String(char* str = '")
	{
		if(str == nullptr)
			_str = "";
		_str = new char[strlen(str)+1];
		strcpy(_str,str);
	}
	String(const String& s)
		:_str(new char[strlen(c._str)+1])
	{
		strcpy(_str,s._str);
	}
	~String()
	{
		if(_str)
			delete[] _str;
	}
private:
	char* _str;
};

String GetString(char* pStr)
{
	String strTemp(pStr);
	return strTemp;
}
	
int main()
{
	String s1("hello");
	String s2(GetString("world"));
	return 0;
}

在上面的代碼中,GetString函數(shù)返回的臨時(shí)對(duì)象,將s2拷貝成功之后,立馬銷(xiāo)毀了(臨時(shí)對(duì)象
的空間被釋放);而s2拷貝構(gòu)造的時(shí),又需要分配空間,一個(gè)剛釋放,一個(gè)又申請(qǐng),有點(diǎn)多此一舉,那能否把GetString返回的臨時(shí)對(duì)象的空間直接交給s2呢?這樣s2也不需要重新開(kāi)辟空間了。

在這里插入圖片描述

移動(dòng)語(yǔ)義:將一個(gè)對(duì)象資源移動(dòng)到另一個(gè)對(duì)象中的方式,在C++中要實(shí)現(xiàn)移動(dòng)語(yǔ)義,必須使用右值引用.

7.2 C++中的右值
右值引用,顧名思義就是對(duì)右值的引用。在C++中右值由純右值和將亡值構(gòu)成。

  • 純右值:用于識(shí)別變量和一些不跟對(duì)象關(guān)聯(lián)的值。比如:常量、運(yùn)算符表達(dá)式等、
  • 將亡值:聲明周期將要結(jié)束的對(duì)象。比如:在值返回時(shí)的臨時(shí)對(duì)象

7.3 右值引用
格式:類(lèi)型&& 應(yīng)用變量名字 = 實(shí)體;
使用場(chǎng)景:
1、與移動(dòng)語(yǔ)義相結(jié)合,減少必須要的資源的開(kāi)辟,提高運(yùn)行效率

String&& GetString(char* pStr)
{
	String strTemp(pStr);
	return strTemp;
}
	
int main()
{
	String s1("hello");
	String s2(GetString("world"));
	return 0;
}

2、給一個(gè)匿名對(duì)象取別名,延長(zhǎng)匿名對(duì)象的生命周期

String GetString(char* pStr) {
return String(pStr);
}
int main()
{
String&& s = GetString("hello");
return 0; }

注意

  • 右值引用在定義時(shí)必須初始化
  • 右值引用不能引用左值
int a = 10;
int&& a1; // 未初始化,編譯失敗
int&& a2 = a; // 編譯失敗,a是一個(gè)左值
// 左值是可以改變的值

7.4 std::move()
C++11中,std::move()函數(shù)位于頭文件中,它可以把一個(gè)左值強(qiáng)制轉(zhuǎn)化為右值引用,通過(guò)右值引用使用該值,實(shí)現(xiàn)移動(dòng)語(yǔ)義。該轉(zhuǎn)化不會(huì)對(duì)左值產(chǎn)生影響.
注意:其更多用在生命周期即將結(jié)束的對(duì)象上。

7.5 移動(dòng)語(yǔ)義中要注意的問(wèn)題
1、在C++11中,無(wú)參構(gòu)造函數(shù)/拷貝構(gòu)造函數(shù)/移動(dòng)構(gòu)造函數(shù)實(shí)際上有三個(gè)版本

Object()
Object(const T&)
Object(T&&)

2、如果將移動(dòng)構(gòu)造函數(shù)聲明為常右值引用或者返回右值的函數(shù)聲明為常量,都會(huì)導(dǎo)致移動(dòng)語(yǔ)義無(wú)法實(shí)現(xiàn)

String(const String&&);
const String GetString();

3、C++11默認(rèn)成員函數(shù),默認(rèn)情況下,編譯器會(huì)隱士生成一個(gè)移動(dòng)構(gòu)造函數(shù),是按照位拷貝來(lái)進(jìn)行。因此在涉及到資源管理時(shí),最好自己定義移動(dòng)構(gòu)造函數(shù)。

class String
{
public:
	String(char* str = "")
	{
		if(str == nullptr)
			str = "";
		_str = new char[strlen(str)+1];
		strcpy(_str,str);
	}
	// 拷貝構(gòu)造
	// String s(左值對(duì)象)
	String(const String& s)
		:_str(new char[strlen(s._str) + 1])
		{
			strcpy(_str,s_str);
		}
	// 移動(dòng)構(gòu)造
	// String s(將亡值對(duì)象)
	String(String&& s)
		:_str(nullptr)
		{
			swap(_str,s._str);
		}
	// 賦值
	String& operator=(const String& s)
	{
		if(this != &s)
		{
			char* tmp = new char[strlen(s._str)+1];
			stcpy(tmp,s._str);
			delete[] _str;
			_str = tmp;
		}
		return *this;
	}
	// 移動(dòng)賦值
	String& operator=(String&& s)
	{
		swap(_str,s._str);
		return *this;
	}
	~String()
	{
		if(_str)
			delete[] _str;
	}
	// s1 += s2 體現(xiàn)左值引用,傳參和傳值的位置減少拷貝
	String& operator+=(const String& s)
	{
		// this->Append(s.c_str());
		return *thisl
	}
	// s1 + s2
	String operator+(const String& s)
	{
		String tmp(*this);
		// tmp.Append(s.c_str());
		return tmp;
	}
	const char* c_str()
	{
		return _str;
	}
private:
	char* _str;
};
int main()
{
	String s1("hello"); // 實(shí)例化s1時(shí)會(huì)調(diào)用移動(dòng)構(gòu)造
	String s2("world");
	String ret 
	ret = s1 + s2 // +返回的是臨時(shí)對(duì)象,這里會(huì)調(diào)用移動(dòng)構(gòu)造和移動(dòng)賦值,減少拷貝
	
	vector<String> v;
	String str("world");
	v.push_back(str); // 這里調(diào)用拷貝構(gòu)造函數(shù)
	v.push_back(move(str)); // 這里調(diào)用移動(dòng)構(gòu)造,減少一次拷貝
	return 0;
}

在這里插入圖片描述

總結(jié):
左值:可以改變的值;
右值: 不可以改變的值(常量,表達(dá)式返回值,臨時(shí)對(duì)象)
左值引用: int& aa = a; 在傳參和傳值的位置使用,減少拷貝,提高效率
右值引用: int&& bb = 10; 在傳值返回和將亡值傳參時(shí),通過(guò)調(diào)用移動(dòng)構(gòu)造和移動(dòng)賦值,減少拷貝,提高效率。
const 左值引用可以引用右值
右值引用可以應(yīng)用move后的左值

7.6 完美轉(zhuǎn)發(fā)
完美轉(zhuǎn)發(fā)是指在函數(shù)模板中,完全按照模板的參數(shù)的類(lèi)型,將參數(shù)傳遞給函數(shù)模板中調(diào)用的另外一個(gè)函數(shù)

void Func(int x)
{
	// ......
}
template<typename T>
void PerfectForward(T t)
{
	Fun(t);
}

PerfectForward為完美轉(zhuǎn)發(fā)的模板函數(shù),F(xiàn)unc為實(shí)際目標(biāo)函數(shù),但上面的轉(zhuǎn)發(fā)還不夠完美,完美轉(zhuǎn)發(fā)是目標(biāo)函數(shù)希望將參數(shù)按照傳遞給轉(zhuǎn)發(fā)函數(shù)的實(shí)際類(lèi)型轉(zhuǎn)給目標(biāo)函數(shù),而不產(chǎn)生額外開(kāi)銷(xiāo),就好像沒(méi)有轉(zhuǎn)發(fā)者一樣.
所謂完美:函數(shù)模板在向其他函數(shù)傳遞自身形參時(shí),如果相應(yīng)實(shí)參是左值,就轉(zhuǎn)發(fā)左值;如果是右值,就轉(zhuǎn)發(fā)右值。(這樣是為了保留在其他函數(shù)針對(duì)轉(zhuǎn)發(fā)而來(lái)的參數(shù)的左右值屬性進(jìn)行不同處理,比如參數(shù)為左值時(shí)實(shí)施拷貝語(yǔ)義、參數(shù)為右值時(shí)實(shí)施移動(dòng)語(yǔ)義)
在C++11中,通過(guò)forward函數(shù)來(lái)實(shí)現(xiàn)完美轉(zhuǎn)發(fā)。

void Fun(int &x){cout << "lvalue ref" << endl;}
void Fun(int &&x){cout << "rvalue ref" << endl;}
void Fun(const int &x){cout << "const lvalue ref" << endl;}
void Fun(const int &&x){cout << "const rvalue ref" << endl;}
template<typename T>
void PerfectForward(T &&t){Fun(std::forward<T>(t));}
int main()
{
PerfectForward(10); // rvalue ref
int a;
PerfectForward(a); // lvalue ref
PerfectForward(std::move(a)); // rvalue ref
const int b = 8;
PerfectForward(b); // const lvalue ref
PerfectForward(std::move(b)); // const rvalue ref
return 0; }

八、lambda表達(dá)式
在C++98中,如果想對(duì)一個(gè)數(shù)據(jù)集合中的元素進(jìn)行排序,可以使用std::sort()方法,但其默認(rèn)按照小于比較,如果想排降序,則要傳入第三個(gè)參數(shù),可以使用std::greater()的比較方法,

vector<int> v{1,4,3,2,7,6,5};
// 默認(rèn)按照小于比較,結(jié)果是升序
sort(v.begin(),v.end());
// 傳入第三個(gè)模板參數(shù)std::greater<T>(),按照大于比較,默認(rèn)是降序
sort(v.begin(), v.end(),greater<int>());

但是該方法只支持內(nèi)置類(lèi)型,對(duì)于用于自定義的類(lèi)型就無(wú)能為力了,這是就需要用于自定義排序時(shí)的規(guī)則。目前我們可以通過(guò)函數(shù)指針,仿函數(shù),lambda來(lái)解決。

1、lambda 表達(dá)式語(yǔ)法

[捕捉列表](參數(shù)列表)mutable->返回值類(lèi)型{函數(shù)體}
捕捉列表:該列表出現(xiàn)在lambda函數(shù)的開(kāi)始位置,編譯器根據(jù)[]來(lái)判斷接下來(lái)的代碼是否為lambda函數(shù),捕捉列表可以捕捉上下文中的變量供lambda函數(shù)使用
參數(shù)列表:與普通函數(shù)的參數(shù)列表一致。則可以連同()一起省略
mutable:默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量性。使用該修飾符,參數(shù)列表不可以省略(即使參數(shù)列表為空)
->返回值類(lèi)型。用于追蹤返回值類(lèi)型。沒(méi)有返回值時(shí)可以省略。返回值類(lèi)型明確的情況下,也可以省略
{函數(shù)體}:在該函數(shù)體,除了可以使用參數(shù)外,也可以使用捕捉到的所有變量

!!!在lambda函數(shù)定義中,參數(shù)列表和返回值類(lèi)型都是可選部分,而捕捉列表和函數(shù)體可以為空。

int main()
{
	// 最簡(jiǎn)單的lambda表達(dá)式
	[]{};
	// 省略參數(shù)列表和返回值類(lèi)型,返回值類(lèi)型有編譯器推演為int
	int a=3,b=4;
	[=]{return a+3;};
	// 省略返回值類(lèi)型
	auto fun1 = [&](int c){b = a + c;};
	// 各部分完整的lambda函數(shù)
	auto fun2 = [=,&b](int c)->int(return += a + c;);
	// 復(fù)制捕捉x
	int x = 10;
	auto add_x = [x](int a)mutable{x *= 2; return a + x;};
	return 0;
}

2、捕獲列表說(shuō)明
捕獲列表描述了上下文中那些數(shù)據(jù)可以被lambda使用,以及使用的方式傳值還是引用

a [var]:表示值傳遞方式捕獲變量var
b [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
c [&var]:表示引用傳遞變量var
d [&]:表示引用傳遞捕獲所有父作用域中的變量(this)
e [this]:表示值傳遞方式捕獲當(dāng)前的this指針

!!!:

a 父作用域包含lambda函數(shù)的語(yǔ)句塊
b 語(yǔ)法上捕獲列表可由多個(gè)捕獲項(xiàng)組成,并以逗號(hào)分隔
	比如:[=,&a,&b]:以引用傳遞的方式捕獲變量a 和 b,值傳遞的方式捕獲其它所有變量.
	[&,a,this];值傳遞的方式捕獲變量a和this,引用方式捕獲其它變量。
	捕捉列表不允許變量重復(fù)傳遞,否則會(huì)導(dǎo)致編譯錯(cuò)誤。比如:[=,a]以傳值的方式捕獲了所有變量,又重復(fù)捕捉a
c 塊作用域以外的lambda函數(shù)捕捉列表必須為空
e 在塊作用域中的lambda函數(shù)僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會(huì)導(dǎo)致編譯報(bào)錯(cuò)
f lambda表達(dá)式之間不能相互賦值,即使看起來(lái)類(lèi)型相同.
void (*PF)();
int main()
{
	auto f1 = []{cout<<"hello world"<<endl;};
	auto f2 = []{cout<<"hello world"<<endl;};
	f1= f2; // 這里會(huì)編譯失敗,提示找不到operator=()
	auto f3(f2); // 允許使用一個(gè)lambda表達(dá)式拷貝一個(gè)新的福分
	PF = f2; // 可以將lambda表達(dá)式賦值給相同類(lèi)型的指針
	return 0;
}

3、lambda表達(dá)式與函數(shù)指針、仿函數(shù)

typedef bool (*GTF) (int, int);
bool greater_func1(int l, int r)
{
	return l > r;
}

struct greater_func2
{
	bool operator()(int l, int r)
	{
		return l > r;
	}
};

int main()
{
	// 函數(shù)指針
	GTF f1 = greater_func1; // typedef 定義
 // bool (*f1) (int, int) = greater_func1; // 不typedef ,直接原生寫(xiě)法,可讀性差
 cout<< f1(1,2)<<endl;
 // 仿函數(shù)
 greater_func2 f2;
 cout<< f2(1,2)<<endl;
 // lamba表達(dá)式
 auto f3 = [] (int l, int r) ->bool{return l > r;};
 cout<< f3(1,2)<<endl;
 
	int a[] = {1,2,4,5,3,7,6,9,8};
	sort(a,a+sizeof(a)/sizeof(a[0]),f1);
	sort(a,a+sizeof(a)/sizeof(a[0]),f2);
	sort(a,a+sizeof(a)/sizeof(a[0]),f3);
	// sort函數(shù)第三個(gè)模板參數(shù)能接受函數(shù)指針,仿函數(shù)、lambda表達(dá)式,是因?yàn)槠涞谌齻€(gè)參數(shù)是一個(gè)模板custom (2)	
	template <class RandomAccessIterator, class Compare>
 void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
 
	return 0;
}

函數(shù)指針,仿函數(shù),lambda用法上是一樣的,但函數(shù)指針類(lèi)型定義很難理解,仿函數(shù)需要實(shí)現(xiàn)運(yùn)算符的重載,必須先定義一個(gè)類(lèi),而且一個(gè)類(lèi)只能實(shí)現(xiàn)一個(gè)()operator的重載。(ep:對(duì)商品的不同屬性實(shí)現(xiàn)比較就需要實(shí)現(xiàn)不同的類(lèi)),要先定義好才能使用。而lambda可以定義好直接使用.

struct Goods
{
	string _name;
	double _price;
	double _appraise;
};

int main()
{
	Goods gds[] = { { "蘋(píng)果", 2.1, 10 }, { "相交", 3, 8 }, { "橙子", 2.2, 7 }, { "菠蘿", 1.5, 10 } };

	sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods& g1, const Goods& g2)->bool
	{return g1._price > g2._price; });

	sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods& g1, const Goods& g2)->bool
	{return g1._appraise > g2._appraise; });

	return 0;
}

上面的例子就體現(xiàn)了其現(xiàn)做現(xiàn)用的特性。

4、lambda表達(dá)式的底層

class Rate
{
public:
Rate(double rate)
 : _rate(rate)
 {}
double operator()(double money, int year)
 {
return money * _rate * year;
 }
private:
double _rate;
};
int main()
{
// 函數(shù)對(duì)象
double rate = 0.49;
Rate r1(rate);
r1(10000, 2);
// 仿函數(shù)
auto r2 = [=](double monty, int year)->double{return monty*rate*year; };
r2(10000, 2);
return 0; }

函數(shù)對(duì)象將rate作為其成員變量,在定義對(duì)象時(shí)候給出初始值即可,lambda表達(dá)式通過(guò)捕獲列表直接捕獲該變量.

在這里插入圖片描述
在這里插入圖片描述

通過(guò)上面的圖可以看出,實(shí)際在底層編譯器對(duì)于處理lambda表達(dá)式的處理方式,完全就是按照函數(shù)對(duì)象的方式處理的,即:如果定義了一個(gè)lambda表達(dá)式,編譯器會(huì)自動(dòng)生成一個(gè)類(lèi),在該類(lèi)中重載了operator();
并且編譯器是通過(guò)lambda_+uuid來(lái)唯一辨識(shí)一個(gè)lambda表達(dá)式的
九、線程庫(kù)
C++11中引入了線程庫(kù),使得在C++在并行編程時(shí)可以不需要依賴(lài)第三方庫(kù),而且在原子操作中引入了原子類(lèi)的概念。
要使用標(biāo)準(zhǔn)庫(kù)中的線程,必須包含頭文件

#include<iostream>
#include<thread>

void fun()
{
	std::cout << "A new thread!" << std::endl;
}
int main()
{
	std::thread t(fun);
	t.join();
	std::cout << "Main thread!" << std::endl;
	system("pause");
	return 0;
}

在這里插入圖片描述
9.1 線程的啟動(dòng)
C++線程庫(kù)通過(guò)構(gòu)造一個(gè)線程對(duì)象來(lái)啟動(dòng)一個(gè)線程,該線程對(duì)象中包含了線程運(yùn)行時(shí)的上下文環(huán)境,如:線程函數(shù)、線程棧、線程其實(shí)狀態(tài)、以及線程ID等,把所有操作全部封裝在一起,在同一傳遞給_beginthreadex()創(chuàng)建線程函數(shù)來(lái)實(shí)現(xiàn)(_beginthreadex是windows中創(chuàng)建線程的底層c函數(shù))
std::thread()創(chuàng)建一個(gè)新的線程可以接受任意的可調(diào)用對(duì)象類(lèi)型,包括lambda表達(dá)式,函數(shù),函數(shù)對(duì)象,函數(shù)指針

// 使用lambda表達(dá)式作為線程函數(shù)創(chuàng)建線程
int main()
{
	int n1 = 1;
	int n2 = 2;
	std::thread t([&](int addNum){n1 += addNum; n2 += addNum; }, 3);
	t.join();
	std::cout << n1 << " " << n2 << std:: endl;
	system("pause");
	return 0;
}

9.1 線程的結(jié)束
啟動(dòng)一個(gè)線程后,當(dāng)線程執(zhí)行完畢時(shí),如果護(hù)手線程使用的資源,thread庫(kù)提供了兩種選擇。
1、join()
join():會(huì)主動(dòng)等待線程終止,在調(diào)用進(jìn)程中join(),當(dāng)新的線程終止時(shí),join()會(huì)清理相關(guān)的資源,然后返回,調(diào)用線程在繼續(xù)向下執(zhí)行。由于join()清理了線程的相關(guān)資源,thread對(duì)象與已銷(xiāo)毀的線程就沒(méi)有關(guān)系了,因此一個(gè)線程對(duì)象每次只能join()一次,如果多次調(diào)用join(),joinable()會(huì)返回false;

int main()
{
	int n1 = 1;
	int n2 = 2;
	std::thread t([&](int addNum){n1 += addNum; n2 += addNum; }, 3);
	std::cout << "join before,joinable=" << t.joinable() << std::endl;
	t.join();
	std::cout << "join after,joinable=" << t.joinable() << std::endl;
	system("pause");
	return 0;
}
// 執(zhí)行結(jié)果:
join before,joinable=1
join after,joinable=0

2、detach()
detach:會(huì)從調(diào)用線程中分離出新的線程,之后不能再與新線程交互。這是調(diào)用joinable()會(huì)返回false。分離的線程會(huì)在后臺(tái)運(yùn)行,其所有權(quán)和控制權(quán)會(huì)交給C++運(yùn)行庫(kù)。C++運(yùn)行庫(kù)會(huì)保證在線程退出時(shí),其相關(guān)資源能正確回收。

int main()
{
	int n1 = 1;
	int n2 = 2;
	std::thread t([&](int addNum){n1 += addNum; n2 += addNum; }, 3);
	std::cout << "join before,joinable=" << t.joinable() << std::endl;
	t.detach();
	std::cout << "join after,joinable=" << t.joinable() << std::endl;
	system("pause");
	return 0;
}

注意,必須在thread對(duì)象銷(xiāo)毀之前作出選擇,因?yàn)榫€程在join()或detach()之前,就可能已經(jīng)結(jié)束,如果之后在分離,線程可能會(huì)在thread對(duì)象銷(xiāo)毀之后繼續(xù)運(yùn)行。

9.3 原子性操作庫(kù)
多線程最主要的問(wèn)題是共享數(shù)據(jù)帶來(lái)的問(wèn)題(線程安全)。如果數(shù)據(jù)都是只讀的,沒(méi)有問(wèn)題,因?yàn)橹蛔x不會(huì)影響數(shù)據(jù),不會(huì)涉及數(shù)據(jù)的修改,所有線程都會(huì)獲得同樣的數(shù)據(jù)。但是,當(dāng)多個(gè)線程要修改數(shù)據(jù)時(shí),就會(huì)產(chǎn)生很多潛在的麻煩。

int sum = 0;

void fun(size_t num)
{
	for (size_t i = 0; i < num; i++)
		sum++;
}

int main()
{
	std::cout << "before,sum=" << sum << std::endl;
	std::thread t1(fun, 100000000);
	std::thread t2(fun, 100000000);
	t1.join();
	t2.join();
	std::cout << "After,sum=" << sum << std::endl;
	system("pause");
	return 0;
}

當(dāng)fun的參數(shù)比較大時(shí),就會(huì)產(chǎn)生和預(yù)期不相符的結(jié)果.
在C++98中可以通過(guò)加鎖來(lái)保護(hù)共享數(shù)據(jù)。

int sum = 0;
std::mutex m;

void fun(size_t num)
{
	for (size_t i = 0; i < num; i++)
	{
		m.lock();
		sum++;
		m.unlock();
	}
}

雖然加鎖結(jié)果了這個(gè)問(wèn)題:但是它有一個(gè)缺陷:只要有一個(gè)線程在對(duì)sum++的時(shí)候,其它線程就會(huì)阻塞,會(huì)影響程序運(yùn)行的效率,而且鎖如果控制不好,或?qū)е滤妓鞯膯?wèn)題。
因此在C++11中引入了原子操作。對(duì)應(yīng)于內(nèi)置的數(shù)據(jù)類(lèi)型,原子數(shù)據(jù)類(lèi)型都有一份對(duì)應(yīng)的類(lèi)型。

在這里插入圖片描述

要使用以上的原子操作,需要添加頭文件

#include<thread>
#include<mutex>
#include<atomic>

std::atomic_int sum{ 0 };

void fun(size_t num)
{
	for (size_t i = 0; i < num; i++)
	{
		sum ++; // 原子的
	}
}

int main()
{
	std::cout << "before,sum=" << sum << std::endl;
	std::thread t1(fun, 10000000);
	std::thread t2(fun, 10000000);
	t1.join();
	t2.join();
	std::cout << "After,sum=" << sum << std::endl;
	system("pause");
	return 0;
}

到此這篇關(guān)于詳解C++之C++11的牛逼特性的文章就介紹到這了,更多相關(guān)C++11特性?xún)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++智能指針shared_ptr

    C++智能指針shared_ptr

    這篇文章主要介紹了C++智能指針shared_ptr,C++11中包括shared_ptr在內(nèi)的多種指針,都是模板類(lèi)型,意味著使用者可以指定想要操作的類(lèi)型下文從shared_ptr創(chuàng)建方式展開(kāi)全文,介紹詳細(xì)具有一的參考價(jià)值,需要的小伙伴可以參考一下
    2022-03-03
  • C語(yǔ)言文件操作函數(shù)大全(超詳細(xì))

    C語(yǔ)言文件操作函數(shù)大全(超詳細(xì))

    本篇文章是對(duì)C語(yǔ)言中的文件操作函數(shù)進(jìn)行了詳細(xì)的總結(jié)分析,需要的朋友參考下
    2013-05-05
  • C語(yǔ)言中字符串和數(shù)字的相互轉(zhuǎn)換實(shí)現(xiàn)代碼

    C語(yǔ)言中字符串和數(shù)字的相互轉(zhuǎn)換實(shí)現(xiàn)代碼

    以下是對(duì)C語(yǔ)言中字符串和數(shù)字的相互轉(zhuǎn)換實(shí)現(xiàn)代碼進(jìn)行了分析介紹,需要的朋友可以參考下
    2013-07-07
  • C語(yǔ)言通過(guò)案例講解并發(fā)編程模型

    C語(yǔ)言通過(guò)案例講解并發(fā)編程模型

    所謂并發(fā)編程是指在一臺(tái)處理器上“同時(shí)”處理多個(gè)任務(wù)。并發(fā)是在同一實(shí)體上的多個(gè)事件。多個(gè)事件在同一時(shí)間間隔發(fā)生,下面我們根據(jù)樣例來(lái)理解
    2022-04-04
  • Qt實(shí)現(xiàn)轉(zhuǎn)動(dòng)輪播圖

    Qt實(shí)現(xiàn)轉(zhuǎn)動(dòng)輪播圖

    這篇文章主要為大家詳細(xì)介紹了Qt實(shí)現(xiàn)轉(zhuǎn)動(dòng)輪播圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • C語(yǔ)言實(shí)現(xiàn)兩個(gè)矩陣相乘

    C語(yǔ)言實(shí)現(xiàn)兩個(gè)矩陣相乘

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)兩個(gè)矩陣相乘的程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • 基于C語(yǔ)言實(shí)現(xiàn)井字棋游戲

    基于C語(yǔ)言實(shí)現(xiàn)井字棋游戲

    這篇文章主要為大家詳細(xì)介紹了基于C語(yǔ)言實(shí)現(xiàn)井字棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Qt學(xué)習(xí)教程之表格控件螞蟻線詳解

    Qt學(xué)習(xí)教程之表格控件螞蟻線詳解

    如果有用過(guò)PS的選區(qū)工具應(yīng)該就會(huì)知道螞蟻線是什么東西了,就是用來(lái)表示選區(qū)的一種虛線,關(guān)鍵還是要?jiǎng)討B(tài)的!下面這篇文章主要給大家介紹了關(guān)于Qt學(xué)習(xí)教程之表格控件螞蟻線的相關(guān)資料,需要的朋友可以參考下
    2018-07-07
  • C語(yǔ)言示例代碼講解棧與隊(duì)列

    C語(yǔ)言示例代碼講解棧與隊(duì)列

    棧和隊(duì)列,嚴(yán)格意義上來(lái)說(shuō),也屬于線性表,因?yàn)樗鼈円捕加糜诖鎯?chǔ)邏輯關(guān)系為?"一對(duì)一"?的數(shù)據(jù),但由于它們比較特殊,本章講解分別用隊(duì)列實(shí)現(xiàn)棧與用棧實(shí)現(xiàn)隊(duì)列
    2022-05-05
  • C++實(shí)現(xiàn)帶頭雙向循環(huán)鏈表的示例詳解

    C++實(shí)現(xiàn)帶頭雙向循環(huán)鏈表的示例詳解

    這篇文章主要介紹了如何利用C++實(shí)現(xiàn)帶頭雙向循環(huán)鏈表,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-12-12

最新評(píng)論