雖然C++11的標準已經公佈很久了,連C++14都有了,但由於C++98實在寫太久,一些新特性還是無法很習慣性的去使用,往往還是走到舊套路去。
這裡特別紀錄一下讀完書後覺得比較重要會常用到的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了!!!! (搞錯重點
這篇文章已經描述的很完整就不重複了
留言列表