1. 程式人生 > >14.C++11 新標準:foreach、右值引用&&、lambda表示式、語言級別的執行緒與鎖

14.C++11 新標準:foreach、右值引用&&、lambda表示式、語言級別的執行緒與鎖

關鍵字和新語法

1、delete      指定刪除類的成員方法        

在類的成員方法後面寫  = delete  就將該成員方法刪除,使用者無法呼叫。

智慧指標unique_ptr就是將物件的拷貝構造和operator=  delete刪除了

2、auto        自動的 根據右表示式,自動推倒出左邊變數的型別。

auto it = vec.begin()   根據等號右側的函式推出左側的型別
相當於 vector<int>::iterator it = vec.begin()

3、nullptr     專門給指標賦空值,不是0,不會像NULL一樣,無法區分是整數0還是0地址。  NULL是巨集定義的0.

以後寫char *p = NULL;
就應該寫成 char *p = nullptr;

4、foreach    以簡單的方式遍歷容器   其底層都轉換成了迭代器的遍歷方式。

int array[20] = { 12, 4, 5, 67, 8, 90 };

雖然叫foreach但使用方式如下:
for (int val : array)  相當於for(int*p = array; p<array+20; ++p)
{
    cout << val << " ";
}
cout << endl;

5、右值引用 &&

const int &c = 20;  左值引用  不能修改臨時量的值

int &&d = 20;         右值引用  可以修改臨時量的值

對編譯器來講這倆玩意一樣。   

1)右值引用可以直接接受無法取地址的東西。而普通的引用右側需要有地址。

int &sd = 10; 報錯

int &&d = 20;

2)有地址的匹配左值引用,不能匹配右值引用。

      沒地址的優先匹配右值引用,沒有右值引用,則匹配左值引用。

void func(const int &a)
{
	cout << "func(const int&)" << endl;
}
void func(int &&a)
{
	cout << "func(int&&)" << endl;
}
int main()
{
	int a = 10;
	func(a);      有地址匹配func(const int&)
	func(30);     立即數沒有地址優先匹配func(int&&),如果沒有該函式則匹配func(const int&)

	return 0;
}

3)可以使用在拷貝構造和賦值函式傳參裡面使用右值引用(效率高)。

當然原來的不要刪,解決淺拷貝問題,要兩個同時有,編譯器會自己使用效率高的方法。

如果右值是臨時物件(也就是馬上就要消失的對像)的話,編譯器會自動呼叫使用右值引用的拷貝構造與賦值。

class Test
{
public:
	Test(int data=10) :ptr(new int(data))
	{
		cout << "Test()" << endl;
	}
	~Test()
	{
		delete ptr;
		cout << "~Test()" << endl;
	}
	Test(const Test &src)  左值拷貝構造
	{
		ptr = new int(*src.ptr);  防止淺拷貝的發生,申請與原來一樣大的空間。
		cout << "Test(const Test&)" << endl;
	}
	Test(Test &&src)      右值拷貝構造
	{
		ptr = src.ptr;   直接指向
		src.ptr = NULL;  並讓原來的指向空,因為能呼叫右值的肯定是即將要銷燬的。
		cout << "Test(Test&&)" << endl;
	}
	Test& operator=(const Test &src)  左值賦值過載
	{
		cout << "operator=(const Test&)" << endl;
		if (this == &src)
                {
			return *this;
                }
		delete ptr;

		ptr = new int(*src.ptr);
		return *this;
	}
	Test& operator=(Test &&src)  右值賦值過載
	{
		cout << "operator=(Test&&)" << endl;

		delete ptr;

		ptr = src.ptr;
		src.ptr = NULL;

		return *this;
	}
private:
	int *ptr;
};

4)右值引用不會預設產生。

臨時物件  =》  拷貝構造或者operator=的時候,一律使用右值引用引數的成員方法

5)move

可以將普通物件轉換為臨時物件,可以引用右值引用的賦值。

再前面智慧指標博文講到unique_ptr,其中體到可以使用move函式來進行許可權的轉移,就是利用右值引用的內容,進行的轉移。

6、智慧指標:

auto_ptr    :     直接讓最新的auto_ptr唯一持有資源

scoped_ptr  :  把拷貝構造和operator=給private化了

unique_ptr  :   禁止了通作用域物件的拷貝構造和operator=  delete刪除了
                       但是提供了右值引用版本的拷貝構造和operator=

7、lambda表示式

以函式的形式存在於表示式中。相當於函式物件。

可以方便的定義和建立匿名函式

[捕獲外部變數列表] (形參列表) -> 返回型別 { 函式體 }

bool cmp(int a, int b)
{
    return  a < b;
}

int main()
{
    泛型演算法中:
    sort(vec.begin(), vec.end());預設是從小到大排序
    
    其本質就是:
    sort(vec.begin(), vec.end(),less<int>());
    那自己寫的函式解釋上面的:
    sort(myvec.begin(), myvec.end(), cmp); // 舊式做法

    如果你想從大到小排序:
    sort(vec.begin(), vec.end(),greater<int>());

    如果使用lambda表示式:
    sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; });   // Lambda表示式
}

8、語言級別的執行緒、鎖

而在C++中無法使用linux中的執行緒與鎖。而C++11中為我們提供了語言級別的執行緒與鎖。使用方式與linux中一致。

語言級別的執行緒:
#include <thread>

void threadfun1()
{
	cout << "threadfun1 - 1\r\n" << endl;
	this_thread::sleep_for(chrono::seconds(1));  sleep1秒
	cout << "threadfun1 - 2" << endl;
}


void threadfun2(int iParam, std::string sParam)
{
	cout << "threadfun2 - 1" << endl;
	this_thread::sleep_for(chrono::seconds(5));  sleep5秒
	cout << "threadfun2 - 2" << endl;
}

int main()
{
	thread t1(threadfun1);             建立執行緒t1,相當於Linux中:pthread_create
	thread t2(threadfun2, 10, "abc");  建立執行緒t2

	t1.join();                         執行緒懸掛,相當於Linux中:pthread_join
	cout << "join" << endl;

	t2.detach();                       成執行緒分離,相當於Linux中:pthread_detach
	cout << "detach" << endl;

	cout << "main thread done!" << endl;
}

標頭檔案:
#include <thread>
#include <mutex>

以一個任務的形式理解:
模擬三個視窗賣票,要求,1.每個視窗都能賣票, 2.票的序號列印應該遞減的
每賣完一張票,視窗休息50ms
睡眠的程式碼:this_thread::sleep_for(std::chrono::seconds(1));

注:通常情況下我們執行程式碼前先上鎖lock(),執行完後再解鎖unlock()。

但著也會出現程式異常終止,導致死鎖的情況。所以我們使用更為優秀的   lock_guard與unique_lock。

unique_lock 與lock_guard都能實現自動加鎖與解鎖功能,但是std::unique_lock要比std::lock_guard更靈活,但是更靈活的代價是佔用空間相對更大一點且相對更慢一點。

語言級別的鎖:

// 定義一個執行緒共享的互斥鎖   lock_guard   unique_lock
mutex thread_mutex;

// 車站視窗賣票
int ticket_count = 10;

// 執行緒函式
void sell_ticket_window(int no)
{
	while (ticket_count > 0)
	{
		//thread_mutex.lock();一般情況下的加鎖
		{ 你會發現這裡有個括號不知道是幹嘛的。巧妙了,出了這個括號相當於出了作用域,鎖自己就釋放了
			//lock_guard<mutex> lock(thread_mutex); 可以類比於socped_ptr
			unique_lock<mutex> lock(thread_mutex);  可以類比於unique_ptr
			cout << "視窗" << no << "賣出第" << ticket_count << "張票!" << endl;
			if (ticket_count > 0)
                        {
				ticket_count--;
                        }
			//thread_mutex.unlock();一般情況下的解鎖
		}
		this_thread::sleep_for(chrono::milliseconds(50));休息50ms 
	}
}

int main()
{
	thread t1(sell_ticket_window, 1);//建立三個執行緒,模擬三個視窗
	thread t2(sell_ticket_window, 2);
	thread t3(sell_ticket_window, 3);

	t1.join();
	t2.join();
	t3.join();

	return 0;
}