在C++98基礎上學習C++11新特性

本身一直用的是C++98規範來編程,對於C++11只聞其名卻沒用過其特性。近期由於工做的須要,須要掌握C++11的一些特性,因此查閱了一些C++11資料。由於本身有C++98的基礎,因此從C++98過渡到C++11並不算特別吃力,讀了一些書籍後,算是對C++11有了個比較基礎的理解,感受程序員仍是要常常保持新語言新特性的更新,如今 C++ 標準都出到C++17了!這篇文章就是對C++11一些經常使用新特性的一些總結,以C++98C++11在語法上的差別來突出C++11新特性的非凡優點。ios

1、新語法

1.自動類型推導auto

auto的自動推導,用於從初始化表達式中推斷出變量的數據類型。c++

//C++98
int a = 10;
string s = "abc";
float b = 10.0;
vector<int> c;
vector<vector<int> > d;
map<int, string> m;
m[1] = "aaa";
map<int, string>::iterator it = m.begin();


//C++11
auto a1 = 10;  //a1爲int
auto s1 = "abc";  //s1爲string
auto b1 = b;  
auto c1 = c;
auto d1 = d;
auto e1 = 'a';
int* x = &a1;
auto d1 = x;
auto m1 = m.begin();
auto x=1,y=2; //ok
auto i=1.j=3.14; //compile error

double a2 = 3.144;
const auto a3 = a2;  //const double
auto a4 = a2;  //double
volatile int c2 = 3;
auto c3 = c2; //int

2.萃取類型decltype

decltype能夠經過一個變量或表達式獲得類型。程序員

#include <iostream>
#include <vector>

using namespace std;

int add(int a)
{
    return ++a;
}

void fun(int a)
{
    cout << "call function: [int]\n" << endl;
}

void fun(int *a)
{
    cout << "call function: [int*]\n" << endl;
}

int main()
{
    //C++11
    int aa = 10;
    decltype(aa) bb = 11;
    string ss = "hello intel";
    decltype(ss) ss1 = "hello";
    const vector<int> vec(1);
    decltype(vec[0]) cc = 1;
    decltype(0) dd = vec[0];  //dd是int類型
    decltype(add(1)) ee;  //int
    int a[5];
    decltype(a) ff; //int[5]
    //decltype(fun) gg;  沒法經過編譯,是個重載函數
    
    return 0;
}

3.nullptr

空指針標識符nullptr是一個表示空指針的標識,他不是一個整數,這是與咱們經常使用的NULL宏的區別。NULL只是一個定義爲常整數0的宏,而nullptr是C++11的一個關鍵字,一個內建的標識符。算法

#include <iostream>
#include <vector>

using namespace std;

void fun(int a)
{
    cout << "call function: [int]\n" << endl;
}

void fun(int *a)
{
    cout << "call function: [int*]\n" << endl;
}

int main()
{
    //C++11
    fun(NULL);   //call function: [int]
    fun(nullptr);  //call function: [int*]

    int* p = NULL;
    fun(p);  //call function: [int*]

    return 0;
}

4.區間迭代range for

C++98C++11在使用語法上的差別以下:編程

#include <iostream>
#include <vector>

using namespace std;


int main()
{
    //C++98
    vector<int> vec(8, 1);
    cout << "C++98 range for:" << endl;
    for (vector<int>::iterator it = vec.begin(); it != vec.end(); it++)
    {
        cout << *it << endl;
    }

    //C++11
    cout << "C++11 range for:" << endl;
    for (auto d : vec)
    {
        cout << d << endl;
    }

    return 0;
}

值得指出的是,是否可以使用基於範圍的for循環,必須依賴一些條件。首先,就是for循環迭代的範圍是可肯定的。對於類來講,若是該類有begin和end函數,那麼for_each之間就是for循環迭代的範圍。對於數組而言,就是數組的第一個和最後一個元素間的範圍。其次,基於範圍的for循環還要求迭代的對象實現+ + 和==等操做符。對於STL中的容器,如string、array、map等使用起來是不會有問題的。下面是C++11操做vector和數組的實踐:數組

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> vec(8, 1);

    //C++11
    cout << "C++11 value range for:" << endl;
    /*d非引用,修改d不會影響vector裏的值*/
    for (auto d : vec)   //d中存儲的是vec中的值
    {
        d = 2;
    }

    for (auto d : vec)   
    {
        cout << d << endl;
    }

    cout << "C++11 reference range for:" << endl;
    /*當迭代變量d爲引用時,vector裏的值能夠被修改*/
    for (auto &d : vec)  
    {
        d = 2;
    }

    for (auto d : vec)   
    {
        cout << d << endl;
    }

    //數組for_each
    char arr[] = {'a','b','c','d'};
    for (auto &d : arr)
    {
        d -= 32;
    }
    for (auto d : arr)
    {
        cout << d << endl;
    }

    //遍歷二維數組,注意迭代變量row必須爲引用。若是你想用 range for 的方法,來遍歷更高維的數組 (dim > 2),那麼你只須要:除了最內層循環以外,其餘全部外層循環都加入 '&' 便可。
    int array2[5][5] = {0};
    for (auto &row : array2)
        for (auto col : row)
            cout << col << endl;

    return 0;
}

5.返回類型後置語法

先看下面這個例子,編譯器在推導decltype(t1+t2)時表達式中t1和t2都未聲明,因此編譯失敗。安全

#include <iostream>
#include <vector>

using namespace std;

template<class T1, class T2>
decltype(t1 + t2) sum(T1 t1, T2 t2)
{
    return t1 + t2;
}

int main()
{
    auto total = sum(1, 2);
    cout << total << endl;
    return 0;
}

因此C++11引入新語法,即把函數的返回值移至參數聲明以後,複合符號->decltype(t1+t2)被稱爲追蹤返回類型。而本來的函數返回值由auto佔據。ide

#include <iostream>
#include <vector>

using namespace std;

template<class T1, class T2>
auto sum(T1 t1, T2 t2) ->decltype(t1+t2)
{
    return t1 + t2;
}

int main()
{
    auto total = sum(1, 2);
    cout << total << endl;
    return 0;
}

6.final和override

struct B
{
    virtual void f1(int) const;
    virtual void f2();
    void f3();
};

struct D1 : public B
{
    void f1(int) const override;  //ok
    void f2(int) override;   //error,B中沒有形如f2(int)的函數
    void f3() override;  //error,f3不是虛函數
    void f4() override;  //error,B中無f4函數
};

struct D2 : public B
{
    void f1(int) const final;  //不準後續的其餘類覆蓋
};

struct D3 :public D2
{
    void f2();
    void f1(int) const; //error,final函數不可覆蓋
};

final還能夠用於防止繼承的發生函數

class NoDerived final
{

};

class Bad :NoDerived   //NoDerived不可作基類
{

};

class Base
{

};

class Last final :Base
{

};

class Bad2 :Last   //Last不可作基類
{

};

7.=default和=delete

對於 C++ 的類,若是程序員沒有爲其定義特殊成員函數,那麼在須要用到某個特殊成員函數的時候,編譯器會隱式的自動生成一個默認的特殊成員函數,好比拷貝構造函數,或者拷貝賦值操做符。性能

C++11容許咱們使用=default來要求編譯器生成一個默認構造函數,也容許咱們使用=delete來告訴編譯器不要爲咱們生成某個默認函數

class B
{
    B() = default; //顯示聲明使用默認構造函數
    B(const B&) = delete; //禁止使用類對象之間的拷貝
    ~B() = default;  //顯示聲明使用默認析構函數
    B& operator=(const B&) = delete;  //禁止使用類對象之間的賦值
    B(int a);  
};

8.lambda表達式

簡單來講,Lambda函數也就是一個函數(匿名函數),它的語法定義以下:

[capture](parameters) mutable ->return-type{statement}
  1. [=,&a,&b]表示以引用傳遞的方式捕捉變量a和b,以值傳遞方式捕捉其它全部變量;
  2. [&,a,this]表示以值傳遞的方式捕捉變量a和類的this指針,引用傳遞方式捕捉其它全部變量。
#include <iostream>

using namespace std;

int main()
{
    auto f = []() {cout << "hello world!" << endl; };
    f();  //hello world!

    int a = 123;
    auto f1 = [a] { cout << a << endl; };
    f1();  //123

    auto f2 = [&a] {cout << a << endl; };
    a = 789;
    f2();  //789

    //隱式捕獲:讓編譯器根據函數體中的代碼來推斷須要捕獲哪些變量
    auto f3 = [=] {cout << a << endl; };
    f3();  //789

    auto f4 = [&] {cout << a << endl; };
    a = 990;
    f4();  //990

    auto f5 = [](int a, int b)->int {return a + b; };
    printf("%d\n", f5(1, 2));  //3

    return 0;
}

lambda表達式在C++下的應用,排序

#include <stdio.h>
#include <algorithm>
#include <vector>

using namespace std;

void print(char arr[], int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

bool cmp(char a, char b)
{
    if (a > b)
        return true;
    else
        return false;
}

int main()
{
    //c++98
    char arr1[] = { 2,5,2,1,5,89,36,22,89 };
    int len = sizeof(arr1) / sizeof(char);
    sort(arr1, arr1 + len, cmp);
    print(arr1, len);

    //c++11
    char arr2[] = { 2,5,2,1,5,89,36,22,89 };
    int len2 = sizeof(arr2) / sizeof(char);
    sort(arr2, arr2 + len2, [](char a, char b)->bool {return a > b; });
    print(arr2, len2);
    return 0;
}

9.std::move

std::move是爲性能而生,經過std::move,能夠避免沒必要要的拷貝操做。std::move是將對象的狀態或者全部權從一個對象轉移到另外一個對象,只是轉移,沒有內存的搬遷或者內存拷貝。

#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
    std::string str = "Hello";
    std::vector<std::string> v;
    //調用常規的拷貝構造函數,新建字符數組,拷貝數據
    v.push_back(str);
    std::cout << "After copy, str is \"" << str << "\"\n"; //After move, str is "Hello"
    //調用移動構造函數,掏空str,掏空後,最好不要使用str
    v.push_back(std::move(str));
    std::cout << "After move, str is \"" << str << "\"\n";   //After move, str is ""
    std::cout << "The contents of the vector are \"" << v[0]
        << "\", \"" << v[1] << "\"\n";   //The contents of the vector are "Hello", "Hello"
}

2、STL新內容

1.std::array

  1. 使用 std::array保存在棧內存中,相比堆內存中的 std::vector,咱們就可以靈活的訪問這裏面的元素,從而得到更高的性能;同時正式因爲其堆內存存儲的特性,有些時候咱們還須要本身負責釋放這些資源。

  2. 使用std::array可以讓代碼變得更加現代,且封裝了一些操做函數,同時還可以友好的使用標準庫中的容器算法等等,好比 std::sort。

std::array 會在編譯時建立一個固定大小的數組,std::array 不可以被隱式的轉換成指針,使用 std::array 很簡單,只需指定其類型和大小便可:

#include <stdio.h>
#include <algorithm>
#include <array>

void foo(int* p)
{

}

int main()
{
    std::array<int, 4> arr = {4,3,1,2};

    foo(&arr[0]);  //OK
    foo(arr.data());  //OK
    //foo(arr);  //wrong
    std::sort(arr.begin(), arr.end());  //排序

    return 0;
}

2.std::forward_list

std::forward_list 使用單向鏈表進行實現,提供了 O(1) 複雜度的元素插入,不支持快速隨機訪問(這也是鏈表的特色),也是標準庫容器中惟一一個不提供 size() 方法的容器。當不須要雙向迭代時,具備比 std::list 更高的空間利用率。

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <forward_list>

int main()
{
    std::forward_list<int> list1 = { 1, 2, 3, 4 };

    //從前面向foo1容器中添加數據,注意不支持push_back
    list1.pop_front();  //刪除鏈表第一個元素
    list1.remove(3);   //刪除鏈表值爲3的節點
    list1.push_front(2);
    list1.push_front(1);
    list1.push_front(14);
    list1.push_front(17);

    list1.sort();

    for (auto &n : list1)
    {
        if (n == 17)
            n = 19;
    }

    for (const auto &n : list1)
    {
        std::cout << n << std::endl;  //1 2 2 4 14 19
    }

    return 0;
}

3.std::unordered_map和std::unordered_set

無序容器中的元素是不進行排序的,內部經過 Hash 表實現,插入和搜索元素的平均複雜度爲 O(constant),在不關心容器內部元素順序時,可以得到顯著的性能提高。

C++11 引入了兩組無序容器:std::unordered_map/std::unordered_multimap 和 std::unordered_set/std::unordered_multiset。

下面給出unordered_map和unordered_set的使用方法。

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>

void foo(int* p)
{

}

int main()
{
    //unordered_map usage
    std::unordered_map<std::string, int> um = { {"2",2},{"1",1},{"3",3} };

    //遍歷
    for (const auto &n : um)
    {
        std::cout << "key:" << n.first << "  value:" << n.second << std::endl;
    }

    std::cout << "value:" << um["1"] << std::endl;


    //unordered_set usage
    std::unordered_set<int> us = { 2,3,4,1};

    //遍歷
    for (const auto &n : us)
    {
        std::cout << "value:" << n << std::endl;
    }

    std::cout << "value:" << us.count(9) << std::endl; //判斷一個數是否在集合內,1存在0不存在
    std::cout << "value:" << *us.find(1) << std::endl;  //查找一個特定的數是否在集合內,找到就返回該數的迭代器位置

    return 0;
}

3、智能指針

1. std::shared_ptr

shared_ptr使用引用計數,每個shared_ptr的拷貝都指向相同的內存。每使用他一次,內部的引用計數加1,每析構一次,內部的引用計數減1,減爲0時,刪除所指向的堆內存。shared_ptr內部的引用計數是安全的,可是對象的讀取須要加鎖。

#include <stdio.h>
#include <memory>
#include <iostream>

int main()
{
    //auto ptr = std::make_shared<int>(10);
    std::shared_ptr<int> ptr(new int(10));
    std::shared_ptr<int> ptrC(ptr);

    auto ptr2 = ptr;

    {
        auto ptr3 = ptr2;
        std::cout << "pointer1.use_count() = " << ptr.use_count() << std::endl;  //4
        std::cout << "pointer2.use_count() = " << ptr2.use_count() << std::endl;  //4
    }

    std::cout << "pointer1.use_count() = " << ptr.use_count() << std::endl;  //3
    std::cout << "pointer2.use_count() = " << ptr2.use_count() << std::endl;  //3

    int *p = ptr.get(); //獲取原始指針

    std::cout << "pointer1.use_count() = " << ptr.use_count() << std::endl;  //3
    std::cout << "pointer2.use_count() = " << ptr2.use_count() << std::endl;  //3

    return 0;
}

3 2. std::unique_ptr

std::unique_ptr 是一種獨佔的智能指針,它禁止其餘智能指針與其共享同一個對象,從而保證代碼的安全:

#include <stdio.h>
#include <memory>
#include <iostream>

int main()
{
    std::unique_ptr<int> ptr(new int(10));
    //auto ptr2 = ptr; //非法

    //雖然說unique_ptr是不可複製的,但咱們可使用std::move將其獨佔權轉移到其餘的unique_ptr
    auto ptr2(std::move(ptr));
    std::cout << *ptr2 << std::endl;

    return 0;
}

3. std::weak_ptr

先觀察下面的代碼,若是咱們在類father中使用的是shared_ptr son的話,father和son的實例並無順利回收,輸出以下:

father !
son !

以上問題就是shared_ptr的環形引用問題。爲了不shared_ptr的環形引用問題,須要引入一個弱引用weak_ptr, weak_ptr是爲了配合shared_ptr而引入的一種智能指針,弱引用不會引發引用計數增長,它更像是shared_ptr的一個助手而不是智能指針,由於它不具備普通指針的行爲,沒有重載operator*和->,它的最大做用在於協助shared_ptr工做,像旁觀者那樣觀測資源的使用狀況.

#include <iostream>
#include <memory>
using namespace std;

class father;
class son;

class father {
public:
    father() {
        cout << "father !" << endl;
    }
    ~father() {
        cout << "~~~~~father !" << endl;
    }
    void setSon(shared_ptr<son> s) {
        son = s;
    }
private:
    //shared_ptr<son> son;
    weak_ptr<son> son; // 用weak_ptr來替換
};


class son {
public:
    son() {
        cout << "son !" << endl;
    }
    ~son() {
        cout << "~~~~~~son !" << endl;
    }
    void setFather(shared_ptr<father> f) {
        father = f;
    }
private:
    shared_ptr<father> father;
};

void test() {
    shared_ptr<father> f(new father());
    shared_ptr<son> s(new son());
    f->setSon(s);
    s->setFather(f);
}

int main()
{
    test();
    return 0;
}

輸出:

father !
son !
~~~~~~son !
~~~~~father !
相關文章
相關標籤/搜索