C++11特性總彙

绿草蓝天發表於2024-07-26

使用方法
將滑鼠移至 "C++11特性總彙" 上面, 右方出現導航小圖示, 點選後在導航列表右上角點選固定, 再拖至左方空閒區域

該總彙編號與書中一致, 若有不明白的地方請檢視原著
<<深入理解C++11: C++11新特性解析與應用>>

預定義宏
  • 211.預定義宏

  • 212.__func__宏返回當前所在函式或結構體名字

  • 213.#pragma once/_Pragma(“once”)該標頭檔案只編譯一次

  • 214.__VA_ARGS__變長引數宏定義 #define PR(...) printf(__VA_ARGS__)

  • 215.寬窄字串的連線

支援long long int型別

22.long long int n至少有64位

不同型別運算拓寬

23.(int)a + (long long int)b a會被提升為long long型後再進行運算

__cplusplus

24.用於C/C++混合編寫, __cplusplus = 201103L可用作檢測編譯器所支援的C++版本

 #ifdef __cplusplus
 extern "C" {
 #endif
 //some code
 #ifdef __cplusplus
 }
 #endif
assert斷言
  1. (1) 包含在<cassert>的斷言assert(n > 0);執行時若n不大於0則報錯
    (2) 靜態斷言static_assert(sizeof(a) == sizeof(b), "must have same width");編譯時報錯,不可使用變數進行靜態斷言
noexcept關鍵詞
void func() noexcept(true);

若有異常,選擇不丟擲,而是直接終止執行並報錯,括號內常量表示式true=不丟擲, false=丟擲

花括號初始化非靜態成員
 private:
     string name{"ABC"};
允許對非靜態成員使用sizeof
 private:
     int n;      //sizeof(class::n)可編譯透過
友元模板

29.可在模板使用友元friend T;

final/override關鍵詞
 private:
     void func() final;        //派生不可過載
     void oldFunc() override;  //過載必須同名,同引數,同常量性,且被過載的是虛擬函式
預設模版型別
 template<typename T1, typename T2 = int> class C1;  //透過
 template<typename T1 = int, typename T2> class C2;  //報錯,類模版預設型別需要從右往左
 template<typename T1 = int, typename T2> void func(T1 a, T2 b);  //透過,函式模版沒有這規定
外部模版
extern template void func<int>(int);
繼承建構函式
struct B: A{
   using A::A;
};
委派建構函式
class A{
public:
	A()	        : A(1234){}     //先呼叫A(int)
	A(int n1)   : A(n1, 'C'){}  //先呼叫A(int, char)
	A(char c1)  : A(2, c1){}    //先呼叫A(int, char)
private:
	A(int n2, char c2)  : my_int(n2), my_char(c2){}
	int my_int;
	char my_char;
}

應用於模版:

class A{
public:
	A(vector<int> &v)  : A(v.begin(), v.end());
	A(deque<short> &d) : A(d.begin(), d.end());
private:
	template<class T> A(A n1, T n2)	: my_list(n1, n2) {}
	list(int) my_list;
}

捕捉異常:

class A{
public:
	A()
	try : A(1234) {init();}
	catch(...)    {error();}
	A(int n)      {throw 0;}
	//構造時執行error(), 而不執行init()
}
右值引用
  • 331.指標成員複製構造
class A{
public:
	A()        : my_ptr(new int(1234)) {}
	A(A &copy) : my_ptr(new int(*copy.my_ptr)) {}//新申請一塊記憶體,避免重複析構同一塊記憶體
	~A() {delete my_ptr;}
	int *my_ptr;
}

  • 332.移動語義
class A{
public:
	A()	              : my_ptr(new int(1234)) {}
	A(const A& copy)  : my_ptr(*copy.my_ptr) {}
	A(A&& move)       : my_ptr(move.my_ptr) {move.my_ptr = nullptr;}
	~A() {delete my_ptr;}

	int* my_ptr;
}
A GetTmp() {
	A tmp;
	return tmp;
}
int main(){
	A one = GetTmp();
}

1.執行GetTmp(),建立A1並初始化後返回,
2.建立並初始化A2,即A2(A1), 執行的是move而不是copy,它將A1的指標賦值給A2,並將A1的指標清掉,
3.析構A1,由於指標清掉了,所以初始化時的記憶體沒有被釋放
4.建立並初始化one,即one(A2),重複第2步,第3步
5.析構one,此時記憶體才真正被釋放,它是A1初始化時申請的


  • 333.有名字的是左值, 沒名字的是右值, 左值*p=1234, 右值p=1234

  • 334.在<utility>中提供了std::move函式,它將左值強制轉化為右值引用

  • 335.swap(T a, T b)函式使用移動語義進行交換,
    移動建構函式不應該寫丟擲異常,
    編譯選項-fno-elide-constructors關閉最佳化以使用移動/複製語義,否則變數直接替換成右值進行編譯


  • 336.完美轉發

引用摺疊規則:

typedef int T;
typedef T& TR;      //or typedef T&& TR
TR a;               //or TR& a , TR&& a

TR定義為T&時,a的型別都是A&
TR定義為T&&時,TR/TR&&的型別為A&&,TR&的型別為A&


template <typename T, typename U>
void PF(T&& t, U& func){
	func(std::forward<T>(t));
}

forward()move()功能相同,完美轉發能夠把引用型別準確傳給呼叫函式

explicit顯式轉換運算子
class A{}
class B{
public:
   explicit operator A() const {return A();}
}
void func(A a){}
void main(){
   B b;
   A a1(b);                     //透過,直接初始化
   A a2 = b;                    //錯誤
   A a3 = static_cast<A>(b);    //透過,顯式轉換
   func(b);                     //錯誤
}
列表初始化
  1. 標頭檔案<initializer_list>,宣告一個以initialize_list<T>模版類為引數的建構函式,就能夠使自定義類使用列表初始化
void func(initializer_list<int> numbers){}

int main(){
   func({1, 2, 3});
   func({});
}

352.使用花括號初始化可以防止型別收窄

const int x = 1234;
char a = x;      //透過
char b = {x};    //錯誤
char* c = new char(1234);    //透過
char* d = new char{1234};    //錯誤

36.POD型別
37.聯合體
38.使用者自定義字面量

內聯名字空間
inline namespace space1{
class A{};
}

namespace space2{
A a;//A in space1
class A{};
A b;//A in space2;
}
模版的別名
template<typename T> using NewName = std::map<T, char*>;
NewName<int> a;      //等同於std::map<int, char*> a;
SFINAE規則
  1. 特殊場景使用特殊模板版本, 另外則是通用模板版本
struct A{ typedef int my_int;
};

template <typename T>
void func(typename T::my_int) {}      //#1

template <typename T>
void func(T) {}                  //#2

int main(){
   func<A>(1234);        //呼叫#1,因為存在    A::my_int
   func<int>(1234);      //呼叫#2,因為不存在 int::my_int
}
>>右尖括號

41.兩個右尖括號>在模板中不再被判定為右移, 需要右移需要加圓括號()

auto型別推導
int a = 1;
auto b = a;    //b的型別為int

編譯時推導
1): auto不能作函式形參型別
2): auto不能對結構體中的肥靜態成員進行推導
3): auto不能宣告陣列
4): auto不能在例項化模板時作為模板引數

decltype型別推導
int a = 1;
decltype(a) b;                         //b的型別為Int
uding size_t = decltype(sizeof(0));    //與using/typydef合用

編譯時推導
1): decltype不能推導過載的函式
2): decltype將亡值推導為右值引用
3): decltype將左值推導為引用, 如 三元運算子, 帶圓括號的左值, ++i, arr[0], *ptr
4): 以上都不是,則推導為本型別

追蹤返回型別
template<typename T1, typename T@>
auto Func(const T1& a, const T@& b) -> decltype(a + b){
   return a + b;
}

編譯時推導

基於範圍的for迴圈
vector<int> MyVector= {1, 2, 3, 4};
for(auto p : MyVector)
{
   cout << p << endl;    //p是解引用後的物件, 無需再*p解引用
}

條件: 迭代物件要實現++==等運算子,普通已知長度的陣列(未知長度的不行), 類要有begin函式和end函式

強型別列舉
enum class MyEnum: char{ e1, e2, e3};    //定義一個以char為底層實現的強型別列舉
MyEnum a = e1;             //錯誤
MyEnum b = MyEnum::e1;     //透過
MyEnum c = 2;              //錯誤

Myenum d = MyEnum::e2;
if(d > 1){}                //錯誤
if((char)d > 1){}          //透過
智慧指標
unique_ptr<int> up1(new int(11));       //無法被複制
unique_ptr<int> up2 = up1;              //編譯錯誤, 指標唯一
unique_ptr<int> up3 = move(up1);        //up1的控制權轉移給up3
up3.reset();                            //顯式釋放
up1.reset();                            //不會出錯

unique_ptr只允許唯一指標指向記憶體
shared_ptr則允許共享同一塊記憶體,它在實現上採用了引用計數,只有在計數歸零時才真正釋放記憶體
weak_ptr不擁有控制權,其成員函式lock()返回其指向記憶體的一個shared_ptr物件,若其記憶體無效則返回nullptr

constexpr常量表示式

函式:

constexpr int GetConst(){return 1;}

1): 函式體只能有單一的return返回語句
2): 函式必須有返回值(不能為void)
3): 使用前必須已定義,即函式定義寫在呼叫函式前面(放至後面則出錯)
4): return返回語句表示式中必須是一個常量表示式,且不能是執行時函式


:

constexpr int a = 1;

它是編譯時期的值,編譯器可以選擇不為它生成資料


自定義類: 必須對建構函式加上constexpr關鍵詞

struct MyType{
   constrxpr MyType(int x): my_int(x){}
   int my_int;
}
constexpr MyType mt  = {2};

1): 建構函式的函式體必須為空
2): 初始化列表只能由常量表示式來賦值

變長模板
template <typename T1, typename T2>
class A{};

template <typename... SomeType>
class B: private A<SomeType...>{};

B<int, char> xy;

typename之後帶...來說明這是一個引數包,該包名字為SomeType
構造型別B時,會呼叫B的私有基類建構函式,並進行引數包展開
即實際上執行的是A<int, char> xy;


template <typename... B>
class MyClass;

template <typename A, typename... B>
class MyClass<A, B...>: private MyClass<B...>{
   A my_a;
};

template<>
class MyClass<>{};

遞迴定義,在引數個數為0時結束,從右往左

引數包可以展開的位置:
1): 表示式
2): 初始化列表
3): 基類描述列表
4): 類成員初始化列表
5): 模板引數列表
6): 通用屬性列表
7): lambda函式的捕捉列表


template <typename... A>
class MyClass1: private OldClass<A...>{};
Myclass1 C1<X, Y>;
//解包為 class MyClass1: private OldClass<X, Y>{};

template <typename... A>
class MyClass2: private OldClass<A>...{};
Myclass2 C2<X, Y>;
//解包為 class MyClass2: private OldClass<X>, private OldClass<Y>{};

template <typename... A>
int GetSize(A... args){
   int size = sizeof...(A);    //使用sizeof...()獲取引數包的大小(個數)
   return size;
}
原子操作
  1. 標頭檔案<atomic>, <thread>
atomic_int  at1 {0};
atomic<int> at2 {0};

int Set(){
   at1 = 1;
   at2 = 2;
}
int Show(){
   cout << at1 << ", " << at2 << endl;    //可能輸出(1,0)/(0,2)/(1,2)
}
int main(){
   thread t1(Set, 0);
   thread t2(Show, 0);

   t1.join();
   t2.join();
   cout << at1 << ", " << at2 << endl;    //必輸出 1, 2
}

由於編譯器的最佳化,執行時可能會打亂實際的程式碼順序
所以需要顯式使用原子類的成員函式寫入store()和讀取load()
這些函式有2個引數,第一個是值,第二個是操作型別
操作列舉值:
memory_order_relaxed不對執行順序作保證
memory_order_acquire本執行緒中,所有後續的讀操作必須在本條原子操作完成後執行
memory_order_release本執行緒中,所有之前的寫操作完成之後才能執行本條原子操作
memory_order_acq_rel同時包含memory_order_acquirememory_order_release
memory_order_consume本執行緒中,所有後續的有關本原子型別的操作,必須在本條原子操作完成後執行
memory_order_seq_cst全部存取按照順序執行

store()可用:
memory_order_relaxed,
memory_order_release,
memory_order_seq_cst

load()可用:
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_seq_cst

RMW(Read Modify Write)同時讀寫操作可用: 全部

thread_local執行緒區域性變數
int thread_local tl;
quick_exit() / at_quick_exit()快速退出
struct A{}{
   ~A(){};
}
void Func(){}
int main(){
   A a;
   at_quick_exit(Func);      //註冊一個函式,在quick_exit時FILO執行
   quick_exit(0);            //A的解構函式不執行
}
nullptr指標空值
= default = delete 預設函式
class A{
public:
   A() = default;           //使用預設建構函式, 保持POD型別
   A(const A&) = delete;    //刪除該函式, 且禁止過載該函式
};
lambda函式(區域性函式)
int a = 1, b = 2;
auto Func1 = [=]() -> int{
   return a + b;
};
int c = Func1();      //c = 3

auto Func2 = [](int i1, int i2) -> int{
   return i1 + i2;
}
int d = Func2(a, b);  // d = 3

[var]值傳遞方式捕捉變數var
[=]值傳遞方式捕捉所有父作用域的變數,包括this
[this]值傳遞方式捕捉當前this

[&var]引用傳遞方式捕捉變數var
[&]引用傳遞方式捕捉所有父作用域的變數,包括this
[&this]引用傳遞方式捕捉當前this

資料對齊
alignas(double) int a = 1;             //a按照double型別寬度對齊
alignas(alignof(double)) int b = 1;    //效果相同, alignof用於獲取型別的寬度
通用屬性
Unicode
原生字串

參考資料: <<深入理解C++11: C++11新特性解析與應用>>

相關文章