close

雖然C++11的標準已經公佈很久了,連C++14都有了,但由於C++98實在寫太久,一些新特性還是無法很習慣性的去使用,往往還是走到舊套路去。

這裡特別紀錄一下讀完書後覺得比較重要會常用到的C++11新特性:

參考書目:深入理解C++11

 

一、 允許在宣告階段直接給予變數初值

C++11

class Car 
{
public:
   Car(){}

   int mSize = 0;
   string mName{"Benz"};
};

C++98

class Car 
{
public:
   Car():mSize(0),mName("Benz"){}

   int mSize;
   string mName;
};

 

二、允許對模板(Template)使用friend

template <typename T> class Car 
{
public:
   friend T;
};

 

三、增加final/override關鍵字

這明顯是從C#那邊抄過來的,非常重要,final可以阻止函式被覆寫,override可以避免手誤(喂

class Car
{
public:
   void Run() final;
};

class Benz : public Car
{
public:
   void Run(); //編譯失敗無法進行複寫
};

 

class Car
{
public:
   virtual void Run();
};

class Benz : public Car
{
public:
   void Ron() override; //編譯失敗沒有Ron可以覆寫
};

 

四、允許在建構式中加入函式

class Car
{
public:
   Car():Init(){}
   virtual void Init();
};

 

五、容器(含STL)的初始賦值

vector<int> a{1,2,3,4};
map<int,int> b = { {1,1} , {2,2} };

 

六、using取代typedef

using涵蓋了typedef所有功能,此外還有一些只有using才能做到的如模板,下為書中的例子

template<typename T> using MapString = map<T,char*>; MapString<int> a;
MapString<int> numberedString;

 

七、自動推導類型宣告auto

跟C#的var與Go的:=一樣的類型推導,以當下右值來決定

auto i = 5;
auto j = "string"

 

八、加入動態類型decltype

把它理解成可以用以前用typeof出來的型別去宣告一個新變數,配合各種組合技與模板可以很好用

int a;
decltype(a) j = 5;

 

九、for可使用 ":"走訪容器

這個真的省下打很多字,特別是STL的容器,不用在那邊begin() end()半天

vector<int> vec{1,2,3,4,5};
for( auto it : vec )
{
   ...
}

 

十、強類型列舉

在enum後加上class代表為強類型列舉,可以保護在swtich或一些判斷式時不同列舉卻可能等值造成的悲劇(避免隱式轉換)。另外不公開列舉內容,可避免不同列舉撞名問題

enum class AA : int {
    A_1 = 1,
    A_2 = 2,
};
enum class BB : int {
    B_1 = 1,
    B_2 = 2,
};
enum CC : int {
    C_1 = 1,
    C_2 = 2,
};
enum DD : int {
    D_1 = 1,
    D_2 = 2,
};

AA a = AA::A_1;
BB b = BB::B_2;
CC c = CC::C_1;
DD d = DD::D_2;
AA aa = A_1;    //失敗,內容未公開
CC cc = C_1;    //成功,內容公開
if (a < b) ...  //失敗,不同類型
if (c < d) ...  //成功,進行了隱式轉換

 

十一、Smart Point :unique_ptr, shared_ptr, weak_ptr

這個範圍非常大也非常重要,有空再額外寫一篇,網路上其實已經有非常詳細的介紹了,自己土炮SmartPoint來控制物件回收的日子終於過去了。

簡單解釋就是取代C++自行new, delete時,常常遺漏忘記刪除造成的memory leak或操作到已刪除或空指標時引發當機的問題。

但這東西看起來美好,但實際用起來陷阱很多,不夠熟悉的話很容易把自己婊到天荒地老,還是要多注意。

 

十二、可變參數模板tuple

雖然跟匿名類別不完全一樣,但很多利用的場景其實相同,可以用來解決許多古早無意義的為回傳而宣告的類別存在,雖然比不上像Golang那麼彈性,但也足夠了

std::tuple<int,float> Get()
{
    int i = 0;
    float j = 0.0f;
    return std::make_tuple(i,j);
    C++14以後可以這樣
    return { i , j };
}

 

十三、原子操作模版

原子操作意味著不可分割的操作,因此以該模板宣告出來的變數,會在操作前自動進行鎖定,應該是屬於Mutex鎖定形式,具體要翻一下文件才知道

atomic<int> a = 0;
atomic_int b = { 0 };
atomic<vector<int>> vec;

 

十四、原生字串符 R"( ... )"

C++的字串長久以來要加入百分比、冒號等東西一直都很麻煩,常常造成編譯器的識別錯誤,都需要仰賴大量的反斜線來湊成一個完整句子,在加入原生字串符的特性後,可以不在需要那一堆反斜線,畫面上看到什麼就輸出什麼

std::string aa = R"("123 % %% %s 
\t \n {0} " hello world " \
ㄚ    )";
std::cout << aa << std::endl;

 

十五、原生鎖頭lock_guard, unique_lock與shared_lock

以前我們常常自製locker類別,通常做法就是裡面擺一個mutex然後提供lock()/enter()與unlock()/leave()函式,然後由另一個臨時宣告的執行體去啟動它,並自動在臨時類別解構時執行unlock來減少重複的操作

lock_guard就是像後者解構時自動unlock的作法,就不用再自己寫了,下面以boost舉例,比較好用

boost::shared_mutex locker;
...

void function() {
   boost::lock_guard<boost::shared_mutex> scope_lock(locker);
   auto it = mapConn.find(xxx);
   if (it != mapConn.end()) {
      ...
   }
}

lock_guard的缺點是沒辦法自行控制lock/unlock的時機,彈性稍微差一點,因此unique_lock就是用來補足這個缺點的產物

boost::shared_mutex locker;
...

void function() {
   boost::unique_lock<boost::shared_mutex> lock(locker);
   mapConn[x] = xxx;
   lock.unlock();

   ...

   lock.lock();
   mapConn.erase(xx);
}

scope結束後就算忘記unlock,unique_lock也會在解構時自行unlock。

shared_lock是一個提供加速的小甜頭,用在很明確知道接下來的鎖定只是要讀取資料的時機,因此系統會允許多個shared_lock同時進入(有點像semaphore),但假如一有unique_lock或lock_guard進入時,shared_lock則無法進入,直到unique_lock/lock_guard離開。把unique_lock/lock_guard當作讀寫鎖,而shared_lock視為為讀取鎖就會很好理解。

boost::shared_mutex locker;
...

void function() {
   boost::shared_lock<boost::shared_mutex> scope_lock(locker);
   auto it = mapConn.find(xxx);
   if (it != mapConn.end()) {
      ...
   }
}

 

十六、Lambda陳述式

終於C++也有Lambda可以跟Javascript一樣搞金字塔跟callback Hell了!!!! (搞錯重點

這篇文章已經描述的很完整就不重複了

 

arrow
arrow
    文章標籤
    c++ c++11
    全站熱搜

    不來嗯 發表在 痞客邦 留言(0) 人氣()