1 constexpr編譯時期常量
constexpr用於函數:
constexpr int get(){return 10}; int array[get()];//get()返回一個編譯期常量可以用於聲明數組大小 constexpr int a=get();//a是一個編譯期常量 int b=get();//此時get當一個普通函數使用,b不再是常量常量表達式函數必須滿足:
1) 函數體只能有一句return
constexpr int get(){ int a=10;//int a=10不可以,但是像static_assert這樣編譯時確定的還是可以的,assert是運行時斷言也不可以 return 10; }2) 函數必須有返回值,所以constexpr void get();是不可以的
3) 使用前必須定義,即在用函數初始化一個常量時必須先定義
4) return不能返回非常量的表達式的函數、全局數據,如:constexpr int get(){ return fun();}返回若fun不是constexpr函數是不可以的
C++11規定浮點數常量表達式是不允許的,因為浮點環境可能改變
常量表達式值只能被常量表達式賦值,即:constexpr int a=1;是可以的,但是constexpr int a=i;是錯誤的。
C++11中constexpr是不能修飾自定義類型的,但是可以定義常量構造函數:
#include常量表達式用於模板函數using namespace std; struct Date { constexpr Date(int y, int m, int d):year(y), month(m), day(d) {}//常量構造函數 constexpr int GetYear() { return year; } constexpr int GetMonth() { return month; } constexpr int GetDay() { return day; } private: int year; int month; int day; }; constexpr Date PRCfound {1949, 10, 1};//常量類型由常量構造函數 Date PRCfound(1949,10,1)//調用constexpr Date等於調用普通構造函數,也就是說此時constexpr忽略 constexpr int foundmonth = PRCfound.GetMonth();//成員函數也需要constexpr int Getmonth聲明為constexpr int main() { cout << foundmonth << endl; } // 10
struct NotLiteral{ NotLiteral(){ i = 5; } int i; }; templateconstexpr T ConstExp(T t) {//該模板函數用於返回一個constexpr型的NotLiteral return t; } void g() { NotLiteral nl; NotLiteral nl1 = ConstExp(nl);//此時當普通函數使用 constexpr NotLiteral nl2 = ConstExp(nl); // 無法通過編譯,nl不是常量表達式 constexpr int a = ConstExp(1); }
2 變長模板
一個變長的宏__VA_ARGS__可以輸出變長參數。C++11中提出了變長模板參數包和函數參數包。c++11中std::tuple就是個變長參數模板類,它是pair類型的泛化,可以指定多個元組,如:tuple
templateclass tuple; tuple<1,2,3> one;//模板實例化 tuple twol
Elements就是一個模板參數包,有了這個模板包tuple模板類可以接受任意多個參數實例化模板,其本質就是將多個模板參數打包成Elements然後通過解包方式釋放模板參數。
函數參數包如下:
template其中T是模板參數包,args是函數參數包。void f(T...args);
3 原子類型
通常線程間同步通信都會想到mutex,condition_variable這樣的大殺器,但是通常一些簡單的線程同步可以利用原子類型來進行。原子操作:要麼不做,要麼一步完成。關於原子類型及其操作見:點擊打開鏈接,原子操作對於lock free編程大有好處。
這裡講一下內存序memory order,編譯器會對源代碼做一些優化,使得一些語句順序優化後可引起線程間產生錯誤,比如:
#include#include #include using namespace std; atomic a; atomic b; int Thread1(int) { int t = 1; a = t; b = 2; } int Thread2(int) { while(b != 2) ; // 自旋等待 cout << a << endl; // 總是期待a的值為1,但是由於編譯器優化,可能使得b=2在a=t之前執行 } int main() { thread t1(Thread1, 0); thread t2(Thread2, 0); t1.join(); t2.join(); return 0; }
在C++11前linux內核采用了一個叫做內存柵memory barrier來強制匯編代碼執行順序。C++11允許供程序員使處理器以指定的順序執行機器指令的機制memory order。memory order是個枚舉類型,專門針對原子類型的,因為一般變量不用於線程間同步沒必要指定順序,其枚舉值如下:
#includememory_order_release/consume的應用實例:#include #include using namespace std; atomic a; atomic b; int Thread1(int) { int t = 1; a.store(t, memory_order_relaxed); b.store(2, memory_order_release); // 本原子操作前所有的寫原子操作必須完成 } int Thread2(int) { while(b.load(memory_order_acquire) != 2); // 本原子操作必須完成才能執行之後所有的讀原子操作 cout << a.load(memory_order_relaxed) << endl; // 1 } int main() { thread t1(Thread1, 0); thread t2(Thread2, 0); t1.join(); t2.join(); return 0; }
#include#include #include #include using namespace std; atomic ptr; atomic data; void Producer() { string* p = new string("Hello"); data.store(42, memory_order_relaxed); ptr.store(p, memory_order_release); } void Consumer() { string* p2; while (!(p2 = ptr.load(memory_order_consume)))//#1#所有關於後序關於ptr的操作都必須在本原子操作之後 ; assert(*p2 == "Hello"); // 總是相等 assert(data.load(memory_order_relaxed) == 42); // 可能斷言失敗,因為#1#處只保證了ptr的順序,而data.load可能先於ptr.load } int main() { thread t1(Producer); thread t2(Consumer); t1.join(); t2.join(); }
在POSIX中在變量前加一個__thread就聲明該變量為線程私有數據,
基本類型(如(unsigned) int,long, char,指針,c類型的結構體等 )可以采用用 __thread修飾符來定義線程局部變量.
示例如下:
__thread int i; extern __thread struct state s; static __thread char *p;
像 string 等類是不能直接用 __thread 修符的,只能用其指針類型的.如下面的示例是錯誤的.
thread std::string thread_name;
下面是正確的:
thread std::string * p_thread_name;
使用 __thread修飾符來定義一些類的線程局部變量,往往容易造成內存洩漏.
5 線程退出方式
teminate終止進程,它調用底層的abort,它們不會調用任何析構函數,向系統發送一個SIGABRT信號,若程序員定義了該信號的信號處理程序清理資源的話操作系統會釋放資源。它們屬於異常退出。
exit和main中的return一樣是正常退出,會執行析構操作,如果注冊了處理函數則還會執行清理函數。如:
#include但是exit執行析構可能耗時,因此C++11引入了quick_exit,該函數不執行析構只是使程序終止。如:#include using namespace std; void openDevice() { cout << "device is opened." << endl; } void resetDeviceStat() { cout << "device stat is reset." << endl; } void closeDevice() { cout << "device is closed." << endl; } int main() { atexit(closeDevice); atexit(resetDeviceStat);//注冊清理函數,清理函數的執行和注冊時的順序相反 openDevice(); exit(0); }
#include#include using namespace std; struct A { ~A() { cout << "Destruct A. " << endl; } }; void closeDevice() { cout << "device is closed." << endl; } int main() { A a; at_quick_exit(closeDevice);//與quick_exit配套的清除函數的注冊 quick_exit(0); }