C++11筆記

__func__宏

__func__返回當前的函數名,也能夠返回class和struct名。算法

/*返回函數名hello*/
const char* hello()
{
     return __func__;
}
/*返回結構體名foo*/
struct foo
{
     foo():name(__func){}
     const char* name;
}

__VA_ARGS__宏

可變參數宏編程

#define INFO(...) printf(__VA_ARGS)

noexcept

noexcept操做會阻止異常擴散,被noexcept修飾的函數,若是throw()拋出異常,則直接調用std::terminate()結束程序,catch並不能捕獲到該異常。noexcept(false)表示始終拋出異常,noexcept至關於noexcept(true)操做。數組

花括號{}初始化

花括號初始化,統一了C++初始化方式,能夠初始化類非靜態成員變量、結構體、普通變量、對象等,類非靜態變量初始化時,初始化列表後在就地初始化以後起做用。性能優化

struct foo

{

     int a;

     int b;

}f{1, 2};


class test

{

public:

     test(int i):a(i){}

private:

     int a{2};

     int b = 4;

     foo f{3,4};

};


int a{1};

test t{6};

sizeof運算符

sizeof運算符能夠對類成員直接進行求值網絡

class foo
{
public:
     int a;
     int b;
};

std::cout << sizeof(foo::a) << std::endl; //C++11之前不合法

final和override

final阻止派生類對基類函數的重載,override修飾的函數則要求該函數必須正確從基類虛函數繼承。ide

class objcct
{
public:
     virtual void print() = 0;
};

class base : public object
{
public:
     void print() final;
};

class Derive : public base
{
public:
     //編譯錯誤
     void print();
};
struct Base
{
     virtual void Turing() = 0;
     virtual void Dijkstra() = 0;
     virtual void VNeumann(int g) = 0;
     virtual void DKnuth() const;
     void Print();
};

struct DerivedMid : public Base
{
     void VNeumann(double g);
};

struct DerivedTop : public DerivedMid
{
     void Turing() override;
     void Dikjstra() override;           //沒法經過編譯,拼寫錯誤,並不是重載
     void VNeumann(double g) override;   //沒法經過編譯,參數不一致
     void DKnuth() override;             //沒法經過編譯,很是量重載
     void Print() override;              //沒法經過編譯,非虛函數重載
};

繼承構造函數

多重繼承中,若是基類構造函數參數不少,那麼咱們在派生類中構造函數初始化列表中初始化基類,須要書寫大量的代碼,繼承構造函數,只須要進行聲明便可將基類構造函數繼承過來。派生類一旦繼承了基類的構造函數,則派生類不會再自動生成默認構造函數。若是派生類從多個基類中繼承構造函數發生衝突,則須要派生類顯示的定義此構造函數。函數

struct A
{
     A(int i){}
     A(double d, int i) {}
     A(float f, int i, const char* c) {}
};

struct B : A {
     using A::A;
     int d{0};
};

int main(){
     B b(356); //b.i = 356
     B b1(2.3, 234); //b1.d = 2.3, b.i=234;
}

委託構造函數

委託構造函數主要解決,類有多個構造函數,構造函數參數個數不一樣時,須要書寫不少的初始化代碼,以下所示。委託構造函數要早於目標構造函數執行,委託構造函數和初始化列表不能同時出現,不然會出現衝突。一樣一個目標構造函數既可使用委託構造函數初始化,也能夠做爲其餘構造函數的委託構造函數,從而造成一個委託鏈,但不能出現委託構造環。另外咱們能夠用try,catch捕獲委託構造函數拋出的異常。性能

/*C++98寫法*/
class Info{
public:
     Info() : type(1), name('a') { InitRest(); }
     Info(int i) : type(i), name('a') { InitRest(); }
     Info(char e) : type(1), name(e) { InitRest(); }
};

/*C++11寫法*/
class Info{
public:
     Info() : type(1), name('a') { InitRest(); }
     Info(int i) : Info() { type = i; }
     Info(char e) : Info() { name = e; }
};

右值引用

左值通常指有名字的,能夠取地址的值,反之沒法取地址,沒有名字的就是右值。例如:a = b + c; a就是左值,(b + c) 就是右值, 表達式(1 + 2)屬於純右值。左值引用T & a, 右值引用 T && a。引入右值主要爲了引入移動構造函數,C++98中對類中具備指針類型變量時,通常須要重寫拷貝構造函數和重載賦值操做,從新申請一塊內存空間,將原來類指針內容拷貝進去,不理會原來的類是否就要釋放。C++11引入移動構造函數,則能夠從原來的類中將內存空間直接嫁接過來,減小內存的申請釋放操做。優化

class foo
{
public:
     foo(const int a) : m_data(new int(a)){}
     //拷貝構造函數
     foo(const foo& s) : m_data(new int(0))
     {
          if (s.m_data != nullptr)
               *m_data = *(s.m_data);
     }
     
     //移動構造函數
     foo(const foo && s) : m_data(s.m_data)
     {
          m_data = nullptr;
     }

private:
     int* m_data;
};

  上面代碼中foo類是典型的類內包含指針類型的成員函數,C++98中須要實現拷貝構造函數,分配內存,而後copy內存數據。C++11能夠實現移動構造函數,移動構造函數的參數是類的右值類型,所以構造類對象時,傳入的參數是右值,則使用移動構造函數構造對象。上面代碼中若是沒有實現移動構造函數,傳入右值進行構造時,則調用拷貝構造函數。實現類時須要本身考慮是否須要實現移動構造函數,構造新對象時須要考慮是否須要經過移動構造函數構造。移動構造函數最好使用noexcept修飾,保證不會拋出異常,拋出異常很是危險,極可能致使內存泄露。
  C++中提供了將左值轉換爲右值引用的函數std::move(左值),函數返回的是右值引用,以下代碼。模板編程時因爲使用typedef重定義類型,會出現多個引用符號,以下代碼,C++11中規定T和TR中只要出現左值引用,則定義v按照左值引用處理,下面代碼中v和v1均爲左值引用。模板編程時須要對重載函數調用時,須要考慮不一樣參數類型如何進行傳參問題,C++11提供了std::forward函數進行完美轉發,無需考慮引用疊加問題,直接進行參數傳遞。編碼

foo f(3);
foo t(std::move(f)); //std::move()函數將左值f轉換爲右值,觸發foo調用移動構造函數構造t對象。

//引用疊加
typedef T& TR;
TR& v;
TR&& v1;

void DoSomething(int& a){std::cout << "int&" << std::endl; }
void DoSomething(int&& a){std::cout << "int&&" << std::endl; }
void DoSomething(const int & a){std::cout << "const int&" << std::endl; }
void DoSomething(const int && a){std::cout << "const int&&" << std::endl;}

template<typename T>
void PerfectForward(T && t)
{
     DoSomething(std::forward<T>(t));
}

顯式轉換操做符

C++98中能夠顯示轉換內置類型,好比bool,char*等,可是不容許顯示轉換自定義類型,C++11中添加了對自定義顯式轉換操做符支持。explicit關鍵字C++98中只能修飾構造函數,令對象只能進行顯式的構造,C++11對其進行了擴展,能夠修飾自定義轉換操做符函數,令對象只能顯式進行轉換。

class foo
{
public:

     operator bool () const
     {
          return true;
     }
     
     //xxx爲自定義類型,能夠是咱們自定義類,自定結構體
     operator xxx ()
     {
     }
     
     //explicit關鍵字修飾,則foo轉換爲yyy只能經過顯式的轉換,即便用static_cast<yyy>()
     explicit operator yyy ()
     {
     }
};

初始化列表

C++11對初始化方式進行了擴展,使用大括號能夠初始化list, vector, map等數據類型,而且支持自定義類型的初始化。

std::vector<int> v = {1, 2, 3};
std::map<int, int> m = {{1, 2}, {3, 4}, {5, 6}};

模板別名

C++對using進行了擴展可使用using給模板定義別名,達到比typedef更增強大的功能。

template<typename K, typename V>
using IntStrMap = std::map<K, V>;

IntStrMap<int, std::string> m;

SFINEA規則

SFINEA即匹配失敗不是錯誤,下面例子C++98中標準會報編譯錯誤,C++11對進行了擴展,容許編譯經過。

struct Test {
     typedef int foo;
};

template <typename T>
void f(typename T::foo) {}

template <typename T>
void f(T) {}

int main(){
     f<Test>(10);
     f<int>(10);
}

auto關鍵字

auto在C++11中被賦予了新的含義,自動推導變量類型,慎用,若是大量auto出現,勢必會下降程序的可閱讀性,auto並不是萬能,下面列出不能推導的情形。

1.不能做爲函數形參類型推導。 void fun(auto i = 2);
2.結構體中非靜態成員不能推導。 struct { auto i = 1; }
3.不能推導數組類型。 auto a[3] = {0};
4.不能做爲模板實例化的參數。std::vector v = {1};

decltype關鍵字

decltype也是C++11中進行類型推導的關鍵字,auto相似於動態語言中的var,decltype則是從變量或返回值中推導類型。

int i = 0;
decltype(i) j = 1;

float foo() {}
decltype(foo()) a = 1.0;

decltype能夠做爲函數返回值推導,模板中大有用處。

auto foo(int i) -> decltype(i) { return i; }

auto會繼承原來變量的const和volatile屬性,decltype推導時不會繼承原來的變量的const和volatile

for循環

std::vector<int> vec = {0, 1, 2};
for (auto i : vec)
     std::cout << i << std::endl;

強枚舉類型

C++98中枚舉類型做用域是全局的,而且底層長度不定,能夠隱式的進行類型轉換。C++11中定義了一種強類型枚舉。

1.強做用域,強類型枚舉成員的名稱不會被輸出到其父做用域空間。
2.轉換限制,強類型枚舉成員的值不能夠與整型隱式地相互轉換。
3.能夠指定底層類型,強類型枚舉默認的底層類型爲int,可是能夠顯式地指定底層類型,底層類型不能夠是wchar_t類型。

enum class Type:int { General, Light, Medium, Heavy};
Type t = Type::Light;

智能指針

C++11廢棄了auto_ptr指針,從新實現了三種智能指針,其實boost中很早就實現了,用法和原理差很少。

1.unique_ptr 指針只能有一個對象擁有,不能夠共享,不能夠複製,move操做後,原來對象指針將爲空,先的對象將惟一擁有該指針。

unique_ptr<int> up1(new int(11));
unique_ptr<int> up2 = up1; //錯誤沒法複製
unique_ptr<int> up3 = move(up1); //move操做後up3將爲空,*up1將致使運行錯誤

2.shared_ptr 指針能夠被多個對象公用,經過引用計數方式標記,計數爲0時,刪除指針指向內存。
3.weak_ptr 能夠指向shared_ptr對象,可是weak_ptr操做不會影響shared_ptr的計數,一般用於判斷shared_ptr是否有效。lock函數能夠返回一個shared_ptr對象,可是不會增長計數

shared_ptr<int> sp1(new int(22));
weak_ptr<int> wp = sp;
shared_ptr<int> sp2 = wp.lock(); //並不會增長引用計數值
sp.reset();  //釋放智能指針指向內存。

constexpr

constexpr 修飾的變量會被編譯爲常量,前提是它是常量,能夠定義常量表達式函數,常量表達式編譯器能夠進行運算,感受老式const只是定義常量不能進行修改,可是並不能告訴編譯器編譯器參與計算。constexpr卻能夠做爲數組初始化的長度,枚舉類型初始化,switch-case的case表達式經過編譯,還能夠修飾構造函數,從而定義一個constexpr修飾的類對象。

constexpr修飾常量表達式函數必須知足下面條件

1.函數體只有單一的return 返回語句。
2.函數必須有返回值(不能時void函數)。
3.函數使用前必須已有定義,不能只進行聲明。
4.return返回語句表達式中不能使用很是量表達式的函數,全局數據,且必須是一個常量表達式,也就是調用的整個鏈都必須用constexpr修飾。

變長模板參數

C++98若是模板中須要傳遞多個參數都是經過宏或者直接手寫一些特化版本,boost中bind和function實現均是如此,C++11中則提供了變長模板參數。

template<typename ...T> class TempArgs {};
template<typename Head, typename ...Tail>
class TempArgs<Head, Tail...>: private TempArgs<Tail...>
{
      Head head;  
};

template<>
class TempArgs<>{};

//變參模板函數
void print() { std::cout << "end" << std::endl; }
template<typename T1, typename ...T>
void print(T1 value, T... args)
{
    std::cout << value << std::endl;
    print(args...);
}

線程相關

C++11中引入了線程庫,原子操做庫,線程本地存儲等,其中原子庫std::atomic ,首次明確了內存模型,進行性能優化時,能夠考慮使用。線程本地存儲使用thread_local修飾保證線程本地變量。

指針空值nullptr

nullptr特指指針空值,完全和老式的NULL其實就是0決裂,能夠直接使用nullptr給指針類型置空,nullptr和NULL之間不能直接轉換,nullptr是nullptr_t類型,全部的nullptr_t類型都是同樣的。

匿名函數

lambda函數即匿名函數,C++11中一大亮點,之前使用STL中的算法時,須要定義一個仿函數或者是一個全局函數,極不方便,有了匿名函數能夠直接在調用地方書寫函數,方便又清晰。[]中是引用列表,「=」表示值傳遞父做用域變量,「&」表示引用傳遞父做用域,默認lambda是const類型,不容許修改傳入參數值。

auto fn1 = [=](int a) -> int{ return a; };
auto fn2 = [&]() -> int{ return 2; };

std::cout << fn1(1) << std::endl;
std::cout << fn2() << std::endl;

對齊方式

C++98中並無明確對齊方式,全部的對齊都是編譯器隱式完成,也可使用編譯提供的特性,指定對齊方式,例如 #pragma pack(n),特別是struct類型在跨網絡傳輸時要特別注意對齊方式,C++11中使用alignas關鍵字指定對齊方式,alignof計算變量對齊方式。

struct alignas(int) foo
{
     char c;
     int a;
};

std::cout << alignof(foo) << std::endl;

Unicode支持

C++定義原生支持Unicode編碼,char16_t用於存儲UTF-16編碼,char32_t用於存儲UTF-32編碼;前綴表示u8表示UTF-8編碼,u表示UTF-16編碼,U表示爲UTF-32編碼。R前綴支持原生字符串輸出,並不會進行轉義,u8R,uR,UR分別支持UTF-8,UTF-16,UTF-32原生字符串。

相關文章
相關標籤/搜索