溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C++中類的特種函數(shù)生成機(jī)制原理分析

發(fā)布時間:2021-09-14 13:40:01 來源:億速云 閱讀:116 作者:小新 欄目:開發(fā)技術(shù)

這篇文章給大家分享的是有關(guān)C++中類的特種函數(shù)生成機(jī)制原理分析的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

    C++類的特種函數(shù)生成機(jī)制

    規(guī)則

    參考Effective Morder C++上的說明:

    • 默認(rèn)構(gòu)造函數(shù):僅當(dāng)類中不包含用戶聲明的構(gòu)造函數(shù)時才生成。

    • 析構(gòu)函數(shù):默認(rèn)生成,當(dāng)基類的析構(gòu)函數(shù)為虛時,派生類的默認(rèn)析構(gòu)函數(shù)為虛函數(shù)。

    • 拷貝構(gòu)造函數(shù):僅當(dāng)類中不包含用戶聲明的拷貝構(gòu)造函數(shù)時才生成。如果該類聲明了移動操作,那么拷貝構(gòu)造函數(shù)將被定義為刪除的。

    • 拷貝賦值運(yùn)算符:僅當(dāng)類中不包含用戶聲明的拷貝賦值運(yùn)算符時才生成。如果該類聲明了移動操作,那么拷貝賦值運(yùn)算符將被定義為刪除的。

    • 移動構(gòu)造函數(shù)和移動賦值運(yùn)算符:僅當(dāng)類中不包含用戶聲明的拷貝操作、移動操作和析構(gòu)函數(shù)時才生成。

    例子:A BUG

    因為不熟悉析構(gòu)函數(shù)的生成機(jī)制,導(dǎo)致了一個BUG。

    首先,下面的代碼沒有問題,因為數(shù)據(jù)成員m_,所以Widget默認(rèn)也是個只移型別;mm中也可以插入一個由只移型別構(gòu)造的std::pair<int, Widget>,因為pair默認(rèn)支持右值參數(shù)構(gòu)造(可以由只移的Widget構(gòu)造)和自身的移動構(gòu)造函數(shù)(可以移動構(gòu)造到unordered_map中):

    class Widget {
    public:
        Widget() = default;
    //    ~Widget() = default;
    private:
        std::thread m_; // 只移型別
    };
    unordered_map<int, Widget> mm;
    mm.insert({12, Widget()});

    然后,我手賤加了一個默認(rèn)的析構(gòu)函數(shù):

    class Widget {
    public:
        Widget() = default;
        ~Widget() = default;
    private:
        std::thread m_; // 只移型別
    };
    unordered_map<int, Widget> mm;
    mm.insert({12, Widget()}); // error!

    報錯信息極長,核心錯誤是:

    error: no matching function for call to ‘std::unordered_map<int, Widget>::insert(<brace-enclosed initializer list>)'
       45 |     unordered_map<int, Widget> mm;

    可以把std::pair的構(gòu)造單獨(dú)抽出來看到更清晰的報錯信息:

    // 代碼如下:
    make_pair(12, Widget());
    // 報錯如下:
    In template: no matching constructor for initialization of '__pair_type' (aka 'pair<int, Widget>')

    “顯然”,是因為Widget的移動構(gòu)造函數(shù)被隱式刪除了(它既不能拷貝也不能移動了),所以無法由Widget參數(shù)構(gòu)造一個std::pair。

    解決方案就是不要定義析構(gòu)函數(shù),或者顯式定義一個移動構(gòu)造函數(shù):

    class Widget {
    public:
        Widget() = default;
        Widget(Widget&&) = default;
        ~Widget() = default;
    private:
        std::thread m_; // 只移型別
    };
    unordered_map<int, Widget> mm;
    mm.insert({12, Widget()});

    例子:std::mutex和std::thread

    在我做試驗的時候,一開始錯把std::mutex記成了只移型別,定義了一個這樣的類:

    class Widget {
    public:
        Widget() = default;
    private:
        std::mutex m_;
    };
    unordered_map<int, Widget> mm;
    mm.insert({12, Widget()}); // error!

    甚至在我沒有添加析構(gòu)函數(shù)的時候Widget就不能拷貝和移動了。

    看看源碼:

    class mutex : private __mutex_base
    {
      public:
        /* ... */
        mutex() noexcept = default;
        ~mutex() = default;
        mutex(const mutex&) = delete;
        mutex& operator=(const mutex&) = delete;
    	  /* ... */
    }

    顯然,因為mutex自行定義了默認(rèn)的析構(gòu)函數(shù)而且把拷貝構(gòu)造函數(shù)定義為刪除的,那么它的移動構(gòu)造函數(shù)也會被隱式刪除,所以mutex既不能拷貝也不能移動。

    和std::thread源碼比較一下:

    class thread
    {
      public:
      thread() noexcept = default;
      thread(const thread&) = delete;
      thread(thread&& __t) noexcept
      { 
        swap(__t);
      }
      ~thread()
      {
        if (joinable())
          std::terminate();
      }
    }

    雖然std::thread定義了析構(gòu)函數(shù)和刪除的拷貝構(gòu)造函數(shù),但是它顯式定義了移動構(gòu)造函數(shù),這使得它雖然不能拷貝但是可以移動。

    感謝各位的閱讀!關(guān)于“C++中類的特種函數(shù)生成機(jī)制原理分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    c++
    AI