日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

c 11特性學(xué)習(xí)總結(jié)

 印度阿三17 2019-09-07

ubuntu 16.04 自帶gcc 5.4 支持c 11
ubuntu 18.04 自帶gcc 7.3 支持c 14
查看編譯器支持:

c 11
c 14
c 17

c 11 feature

  • nullptr/constexpr
  • enum class
  • auto/decltype
  • for iteration
  • initialize_list
  • lamda
  • template
  • rvalue/move

nullptr

以前的編譯器實(shí)現(xiàn),可能會(huì)把NULL定義為0.所以,當(dāng)你有兩個(gè)同名函數(shù)foo(int),foo(char*)時(shí),foo(NULL)你的本意可能是調(diào)用后者,但實(shí)際調(diào)用的是前者.nullptr的引入就是為了解決這個(gè)問(wèn)題.

void foo(char *ch)
{
    std::cout << "call foo(char*)" << std::endl;
}
void foo(int i)
{
    std::cout << "call foo(int)" << std::endl;
}

void test_nullptr()
{
    if (NULL == (void *)0)
        std::cout << "NULL == 0" << std::endl;
    else
        std::cout << "NULL != 0" << std::endl;
    foo(0);
    //foo(NULL); // 編譯無(wú)法通過(guò)
    foo(nullptr);
}

constexpr

常量表達(dá)式的引入是為了提高性能,將運(yùn)行期間的行為放到編譯期間去完成.如下代碼

constexpr long int fib(int n) 
{ 
    return (n <= 1)? n : fib(n-1)   fib(n-2); 
} 
void test_constexpr()
{
    auto start = std::chrono::system_clock::now();
    const long int res = fib(30);
    auto end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    cout << "elapsed_seconds:"<<elapsed_seconds.count()<<endl;  

    start = std::chrono::system_clock::now();
    long int res2 = fib(30);
    end = std::chrono::system_clock::now();
    elapsed_seconds = end-start;
    cout << "elapsed_seconds:"<<elapsed_seconds.count()<<endl; 

由于傳給fib(int n)的參數(shù)是30,是個(gè)固定的值.所以可以在編譯期間就求出來(lái).當(dāng)我們用const long和long聲明返回值時(shí),對(duì)前者會(huì)在編譯期就做計(jì)算優(yōu)化.如下圖,可以看到二者運(yùn)行時(shí)間有數(shù)量級(jí)上的差異.  

enum class

c 11中把枚舉認(rèn)為是一個(gè)類,以前的標(biāo)準(zhǔn)中枚舉值就是一個(gè)整數(shù)而已.看如下代碼

void test_enum()
{
    enum color {black,white};
    //auto white = true; //redeclared

    enum class Color{r,g,b};
    auto r = 1;
}

以前的標(biāo)準(zhǔn)中enum color {black,white};相當(dāng)于定義了兩個(gè)int變量black,white.所以//auto white = true;編譯期會(huì)報(bào)錯(cuò).引入了enum class則不再有這個(gè)問(wèn)題.

auto/decltype

這個(gè)是c 11中非常重要的一點(diǎn)特性,極大地簡(jiǎn)化了編碼的復(fù)雜.編譯期自動(dòng)去推導(dǎo)變量的類型.再也不需要我們操心了.  
auto做變量類型推導(dǎo),decltype做表達(dá)式類型推導(dǎo).

void test_auto()
{
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);

    for (std::vector<int>::iterator it = v.begin(); it != v.end(); it  )
    {
        cout << *it << endl;
    }

    for (auto it = v.begin(); it != v.end(); it  )
    {
        cout << *it << endl;
    }

    for (auto &i : v)
    {
        cout << i << endl;
        i = 100; //修改掉v中的元素值
    }

    for (auto i : v)
    {
        cout << i << endl; //輸出100
        i = 200;           //不會(huì)修改v中的元素值
    }

    for (auto i : v)
    {
        cout << i << endl; //輸出為100
    }
}

用法如上述代碼所示.比如遍歷vector,寫法由for (std::vector::iterator it = v.begin(); it != v.end(); it )簡(jiǎn)化到for (auto it = v.begin(); it != v.end(); it ),如果配合上for迭代,則進(jìn)一步簡(jiǎn)化到for (auto &i : v).

注意c 11中,auto變量自動(dòng)推導(dǎo)有2個(gè)例外

  • //int add(auto x,auto y); //c 14才支持函數(shù)參數(shù)為auto
  • //auto arr[10] = {0}; //編譯錯(cuò)誤 auto不能用于數(shù)組類型的推導(dǎo)

decltype做表達(dá)式類型推導(dǎo).假設(shè)我們要寫一個(gè)加法的模板函數(shù),比如

template<typename R, typename T, typename U>
R add(T x, U y) 
{
    return x y;
}

對(duì)于返回值類型R的話,我們必須在模板的參數(shù)列表中手動(dòng)指明.調(diào)用的時(shí)候形式則為add<R,T,U>(x,y).而很可能我們并不知道返回類型是什么,比如我使用一個(gè)第三方庫(kù),我只是想對(duì)x,y做一個(gè)add操作,后續(xù)我可能會(huì)從x,y取一些數(shù)據(jù)做后續(xù)處理,此時(shí)我并不關(guān)心add操作返回值類型是什么.  
c 11中用decltype自動(dòng)推導(dǎo)表達(dá)式類型解決這個(gè)問(wèn)題.

template<typename T, typename U>
auto add_cxx11(T x, U y) -> decltype(x y) 
{
    return x y;
}

用decltype(x y)聲明返回值類型,讓編譯器自動(dòng)推導(dǎo)就好了.
在c 14中,有了更好的支持,已經(jīng)不再需要顯示地聲明返回值類型了.

//c  14支持
/* 
template<typename T, typename U>
auto add_cxx14(T x, U y)
{
    return x y;
}
*/

for迭代

基于范圍的for迭代,非常類似與python中的用法了.代碼在前面auto/decltype一節(jié)已經(jīng)展示.需要注意的是for (auto i : v)拿出的i是副本,不會(huì)修改v中的元素的值.for (auto &i : v)拿到的是引用,會(huì)修改掉v中的值.

初始化列表

c 11之前對(duì)象的初始化并不具有統(tǒng)一的表達(dá)形式,比如

   int a[3] = {4,5,6}
   /*
    以前類的初始化只能通過(guò)拷貝構(gòu)造函數(shù)或者()
    比如 */
    class A
    {
        A(int x,int y,int z)
    }
    A b;
    A a(b);
    A a2(1,2,3)
    

c 11提供了統(tǒng)一的語(yǔ)法來(lái)初始化任意對(duì)象.
比如

class XX
{
public:
    XX(std::initializer_list<int> v):v_int(v)
    {
    }

    vector<int> v_int = {3,4,5};
};
XX xxxxxx = {6,7,8,9,10};

或者

    struct A
    {
        int a_;
        int b_;
    };
    A a {1,2};

此外,初始化列表還可以作為函數(shù)的入?yún)ⅲ?/p>

void f_take_initialize(initializer_list<int> list)
{
    int sum = 0;
    for (auto l : list)
    {
        sum  = l;
    }
    cout << sum << endl;
}

void test_initialize()
{
    f_take_initialize({1, 2, 3});
}

using關(guān)鍵字

using并不是c 11才有的,但是c 11中提升了這個(gè)關(guān)鍵字的功能,用于取代typedef,提供更加統(tǒng)一的表達(dá)形式.

template <typename T, typename U>
class SuckType;
typedef SuckType<std::vector<int>, std::string> NewType; 
using NewType = SuckType<std::vector<int>, std::string>;

typedef int(*mycallback)(void*);
using mycallback = int(*)(void*);

lamda表達(dá)式

即匿名函數(shù),這也是c 11中一個(gè)相當(dāng)重要的特性.有的時(shí)候,我們可能需要使用某個(gè)功能,但這個(gè)功能可能只在某一個(gè)函數(shù)內(nèi)部有用,那么我們則沒(méi)有必要去寫一個(gè)全局函數(shù)或者類的成員函數(shù)去抽象這個(gè)功能.這時(shí)候就可以實(shí)現(xiàn)一個(gè)匿名函數(shù).

[捕獲列表](參數(shù)列表) -> 返回類型 
{
// 函數(shù)體
}

匿名函數(shù)的形式如上所示.參數(shù)列表,返回類型都很好理解.默認(rèn)情況下,匿名函數(shù)是不可以使用匿名函數(shù)外部的變量的,捕獲列表就起到一個(gè)傳遞外部參數(shù)的作用
捕獲列表有下述幾種情況

  • 值捕獲
  • 引用捕獲
  • 隱式捕獲
  • 表達(dá)式捕獲 //c 14支持
    值得注意的是,值捕獲的話,值是在匿名函數(shù)定義好的時(shí)候就做傳遞,而不是調(diào)用的時(shí)候做傳遞.
    如下代碼
void test_lamda()
{
    auto add_func = [](int x,int y) -> int{return x y;};
    auto sum = add_func(3,4);
    cout<<"sum="<<sum<<endl;

    int a = 1;
    auto copy_a = [a]{return a;};
    a = 100;
    auto a_i_get = copy_a();
    printf("a_i_get=%d,a=%d\n",a_i_get,a);

    auto copy_a_refrence = [&a]{a=50;return a;};
    auto a_refrence_i_get = copy_a_refrence();
    printf("a_i_get=%d,a=%d\n",a_refrence_i_get,a);

    int b = 1;
    auto f = [&](){return b;} ;

}

注意,第一個(gè)輸出的a_i_get=1而不是100.盡管a是100.這是因?yàn)樵赾opy_a定義的時(shí)候,a的值是1. copy_a_refrence的捕獲列表是引用,函數(shù)體內(nèi)修改a=50,所以輸出的是50 

當(dāng)要捕獲的變量非常多的時(shí)候,一個(gè)個(gè)寫是非常麻煩的,所以可以直接在捕獲列表里用=和&表示傳值和傳引用

[](){}    //傳空
[=](){}  //傳值
[&](){}  //傳引用

變長(zhǎng)模板

template<typename... Ts> class Magic;

c 11之前,模板的參數(shù)是固定個(gè)數(shù)的.c 11之后支持不定長(zhǎng)參數(shù)的模板.用...表示不定長(zhǎng).
c 11標(biāo)準(zhǔn)庫(kù)新引入的數(shù)據(jù)結(jié)構(gòu)tuple就是用了這個(gè)特性實(shí)現(xiàn)的.

move語(yǔ)義和右值引用.

這也是c 11中引入的非常重要的一個(gè)特性.主要作用在于性能的提升.
通俗地講,一個(gè)可以取地址的變量,即為左值,不可以取地址的即為右值.
以之前的vector.push_back()為例,插入的是數(shù)據(jù)的一份拷貝.當(dāng)要插入的數(shù)據(jù)結(jié)構(gòu)本身內(nèi)存特別大的時(shí)候,這種拷貝帶來(lái)的性能消耗是非常大的.  
move的引入即用于解決此類問(wèn)題,move()將一個(gè)值轉(zhuǎn)換為右值. 標(biāo)準(zhǔn)庫(kù)的數(shù)據(jù)結(jié)構(gòu)里實(shí)現(xiàn)了void push_back( T&& value );的版本.
move()名字叫move,但他本身并不做任何move的操作,move更類似于淺拷貝,即拷貝待拷貝內(nèi)容的地址過(guò)去.但是淺拷貝并不會(huì)使得之前的對(duì)象失去所有權(quán),而move會(huì),所以move所做的事情就是資源的所有權(quán)的轉(zhuǎn)移

先看一下這段代碼

void test_move()
{
    std::string str = "Hello world.";
    std::vector<std::string> v;
    // 將使用 push_back(const T&), 即產(chǎn)生拷貝行為
    v.push_back(str);
    // 將輸出 "str: Hello world."
    std::cout << "str: " << str << std::endl;
    // 將使用 push_back(const T&&), 不會(huì)出現(xiàn)拷貝行為
    // 而整個(gè)字符串會(huì)被移動(dòng)到 vector 中,所以有時(shí)候 std::move 會(huì)用來(lái)減少拷貝出現(xiàn)的開(kāi)銷
    // 這步操作后, str 中的值會(huì)變?yōu)榭?    v.push_back(std::move(str));
    // 將輸出 "str: "
    std::cout << "str: " << str << std::endl;
}

即我們前面提到的"hello world"這些內(nèi)容的所有權(quán)的轉(zhuǎn)移.move之后,原先的str已經(jīng)失去了所有權(quán),打印為空.

再來(lái)看一段代碼.

void test_move_efficience()
{
    vector<int> v_i;
    for(auto i = 0;i<10000000;i  )
    {
        v_i.push_back(i);
    }

    auto f = [&](vector<int> v)
             {
                return v;
             };

    auto g = [&](vector<int>& v)
            {
                return v;
            };

    auto start =  std::chrono::system_clock::now();
    f(v_i);
    auto end = std::chrono::system_clock::now();
    std::chrono::duration<double,std::milli> elapsed_seconds = end-start;
    cout << "f(v_i) elapsed_seconds:"<<elapsed_seconds.count()<<endl;  

    start = std::chrono::system_clock::now();
    f(move(v_i));
    end = std::chrono::system_clock::now();
    elapsed_seconds = end-start;
    cout << "f(move(v_i)) elapsed_seconds:"<<elapsed_seconds.count()<<endl;  

    start = std::chrono::system_clock::now();
    g(v_i);
    end = std::chrono::system_clock::now();
    elapsed_seconds = end-start;
    cout << "g(v_i) elapsed_seconds:"<<elapsed_seconds.count()<<endl;  

    list<int> li;
    for(auto i = 0;i<10000000;i  )
    {
        li.push_front(i);
        li.push_back(i 1);
    }
    vector<list<int>> vl;

    start = std::chrono::system_clock::now();
    vl.push_back(li);
    end = std::chrono::system_clock::now();
    elapsed_seconds = end-start;
    cout << "vl.push_back(li) elapsed_seconds:"<<elapsed_seconds.count()<<endl; 

    start = std::chrono::system_clock::now();
    vl.push_back(move(li));
    end = std::chrono::system_clock::now();
    elapsed_seconds = end-start;
    cout << "vl.push_back(move(li)) elapsed_seconds:"<<elapsed_seconds.count()<<endl; 
}

先貼輸出

我們知道,函數(shù)傳參的時(shí)候,值傳遞的話,實(shí)際上是做了一份參數(shù)的拷貝.所以f(v_i)的開(kāi)銷是最高的,達(dá)15ms,f(move(v_i))的話,沒(méi)有了拷貝的操作,耗時(shí)1ms.對(duì)于g(v_i)的話,參數(shù)為引用,沒(méi)有拷貝操作,耗時(shí)只有0.000594ms. 因?yàn)椴还苁莊(),g(),函數(shù)內(nèi)部都是非常簡(jiǎn)單的,沒(méi)有任何操作,而move()本身會(huì)帶來(lái)一定消耗,所以f(move(v_i))耗時(shí)比g(v_i)更高.

而move語(yǔ)義更常見(jiàn)的使用在于標(biāo)準(zhǔn)庫(kù)里的封裝.比如上述代碼,vl.push_back(li)和vl.push_back(move(li))調(diào)用了不同的push_back(). 其性能是有著數(shù)量級(jí)的差異. 以我們的例子為例,其耗時(shí)分別為1207ms和0.001548ms.

所以如果我們自己的數(shù)據(jù)類型,內(nèi)部含有大量的數(shù)據(jù),我們應(yīng)當(dāng)自己去實(shí)現(xiàn)move構(gòu)造函數(shù).這樣使用標(biāo)準(zhǔn)庫(kù)時(shí)才可以更好的提高性能.

來(lái)源:https://www./content-1-442901.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多