詳解在C++中顯式默認設(shè)置的函數(shù)和已刪除的函數(shù)的方法
在 C++11 中,默認函數(shù)和已刪除函數(shù)使你可以顯式控制是否自動生成特殊成員函數(shù)。已刪除的函數(shù)還可為您提供簡單語言,以防止所有類型的函數(shù)(特殊成員函數(shù)和普通成員函數(shù)以及非成員函數(shù))的參數(shù)中出現(xiàn)有問題的類型提升,這會導(dǎo)致意外的函數(shù)調(diào)用。
顯式默認設(shè)置的函數(shù)和已刪除函數(shù)的好處
在 C++ 中,如果某個類型未聲明它本身,則編譯器將自動為該類型生成默認構(gòu)造函數(shù)、復(fù)制構(gòu)造函數(shù)、復(fù)制賦值運算符和析構(gòu)函數(shù)。這些函數(shù)稱為特殊成員函數(shù),它們使 C++ 中的簡單用戶定義類型的行為如同 C 中的結(jié)構(gòu)。也就是說,可以創(chuàng)建、復(fù)制和銷毀它們而無需任何額外編碼工作。C++11 會將移動語義引入語言中,并將移動構(gòu)造函數(shù)和移動賦值運算符添加到編譯器可自動生成的特殊成員函數(shù)的列表中。
這對于簡單類型非常方便,但是復(fù)雜類型通常自己定義一個或多個特殊成員函數(shù),這可以阻止自動生成其他特殊成員函數(shù)。實踐操作:
- 如果顯式聲明了任何構(gòu)造函數(shù),則不會自動生成默認構(gòu)造函數(shù)。
- 如果顯式聲明了虛擬析構(gòu)函數(shù),則不會自動生成默認析構(gòu)函數(shù)。
- 如果顯式聲明了移動構(gòu)造函數(shù)或移動賦值運算符,則:
- 不自動生成復(fù)制構(gòu)造函數(shù)。
- 不自動生成復(fù)制賦值運算符。
- 如果顯式聲明了復(fù)制構(gòu)造函數(shù)、復(fù)制賦值運算符、移動構(gòu)造函數(shù)、移動賦值運算符或析構(gòu)函數(shù),則:
- 不自動生成移動構(gòu)造函數(shù)。
- 不自動生成移動賦值運算符。
注意
此外,C++11 標準指定將以下附加規(guī)則:
- 如果顯式聲明了復(fù)制構(gòu)造函數(shù)或析構(gòu)函數(shù),則棄用復(fù)制賦值運算符的自動生成。
- 如果顯式聲明了復(fù)制賦值運算符或析構(gòu)函數(shù),則棄用復(fù)制構(gòu)造函數(shù)的自動生成。
- 在這兩種情況下,Visual Studio 將繼續(xù)隱式自動生成所需的函數(shù)且不發(fā)出警告。
這些規(guī)則的結(jié)果也可能泄漏到對象層次結(jié)構(gòu)中。例如,如果基類因某種原因無法擁有可從派生類調(diào)用的默認構(gòu)造函數(shù) - 也就是說,一個不采用任何參數(shù)的 public 或 protected 構(gòu)造函數(shù) - 那么從基類派生的類將無法自動生成它自己的默認構(gòu)造函數(shù)。
這些規(guī)則可能會使本應(yīng)直接的內(nèi)容、用戶定義類型和常見 C++ 慣例的實現(xiàn)變得復(fù)雜 — 例如,通過以私有方式復(fù)制構(gòu)造函數(shù)和復(fù)制賦值運算符,而不定義它們,使用戶定義類型不可復(fù)制。
struct noncopyable
{
noncopyable() {};
private:
noncopyable(const noncopyable&);
noncopyable& operator=(const noncopyable&);
};
在 C++11 之前,此代碼段是不可復(fù)制的類型的慣例形式。但是,它具有幾個問題:
復(fù)制構(gòu)造函數(shù)必須以私有方式進行聲明以隱藏它,但因為它進行了完全聲明,所以會阻止自動生成默認構(gòu)造函數(shù)。如果你需要默認構(gòu)造函數(shù),則必須顯式定義一個(即使它不執(zhí)行任何操作)。
即使顯式定義的默認構(gòu)造函數(shù)不執(zhí)行任何操作,編譯器也會將它視為重要內(nèi)容。其效率低于自動生成的默認構(gòu)造函數(shù),并且會阻止 noncopyable 成為真正的 POD 類型。
盡管復(fù)制構(gòu)造函數(shù)和復(fù)制賦值運算符在外部代碼中是隱藏的,但成員函數(shù)和 noncopyable 的友元仍可以看見并調(diào)用它們。如果它們進行了聲明但是未定義,則調(diào)用它們會導(dǎo)致鏈接器錯誤。
雖然這是廣為接受的慣例,但是除非你了解用于自動生成特殊成員函數(shù)的所有規(guī)則,否則意圖不明確。
在 C++11 中,不可復(fù)制的習(xí)語可通過更直接的方法實現(xiàn)。
struct noncopyable
{
noncopyable() =default;
noncopyable(const noncopyable&) =delete;
noncopyable& operator=(const noncopyable&) =delete;
};
請注意如何解決與 C++11 之前的慣例有關(guān)的問題:
仍可通過聲明復(fù)制構(gòu)造函數(shù)來阻止生成默認構(gòu)造函數(shù),但可通過將其顯式設(shè)置為默認值進行恢復(fù)。
顯式設(shè)置的默認特殊成員函數(shù)仍被視為不重要的,因此性能不會下降,并且不會阻止 noncopyable 成為真正的 POD 類型。
復(fù)制構(gòu)造函數(shù)和復(fù)制賦值運算符是公共的,但是已刪除。定義或調(diào)用已刪除函數(shù)是編譯時錯誤。
對于了解 =default 和 =delete 的人來說,意圖是非常清楚的。你不必了解用于自動生成特殊成員函數(shù)的規(guī)則。
對于創(chuàng)建不可移動、只能動態(tài)分配或無法動態(tài)分配的用戶定義類型,存在類似慣例。所有這些慣例都具有 C++11 之前的實現(xiàn),這些實現(xiàn)會遭受類似問題,并且可在 C++11 中通過按照默認和已刪除特殊成員函數(shù)實現(xiàn)它們,以類似方式進行解決。
顯式默認設(shè)置的函數(shù)
可以默認設(shè)置任何特殊成員函數(shù) — 以顯式聲明特殊成員函數(shù)使用默認實現(xiàn)、定義具有非公共訪問限定符的特殊成員函數(shù)或恢復(fù)其他情況下被阻止其自動生成的特殊成員函數(shù)。
可通過如此示例所示進行聲明來默認設(shè)置特殊成員函數(shù):
struct widget
{
widget()=default;
inline widget& operator=(const widget&);
};
inline widget& widget::operator=(const widget&) =default;
請注意,只要特殊成員函數(shù)可內(nèi)聯(lián),便可以在類主體外部默認設(shè)置它。
由于普通特殊成員函數(shù)的性能優(yōu)勢,因此我們建議你在需要默認行為時首選自動生成的特殊成員函數(shù)而不是空函數(shù)體。你可以通過顯式默認設(shè)置特殊成員函數(shù),或通過不聲明它(也不聲明其他會阻止它自動生成的特殊成員函數(shù)),來實現(xiàn)此目的。
注意
Visual Studio 不支持默認的移動構(gòu)造函數(shù)或移動賦值運算符作為 C++11 標準授權(quán)。有關(guān)詳細信息,請參閱 支持 C++11/14/17 功能(現(xiàn)代 C++)中的“默認函數(shù)和已刪除的函數(shù)”一節(jié)。
已刪除的函數(shù)
可以刪除特殊成員函數(shù)以及普通成員函數(shù)和非成員函數(shù),以阻止定義或調(diào)用它們。通過刪除特殊成員函數(shù),可以更簡潔地阻止編譯器生成不需要的特殊成員函數(shù)。必須在聲明函數(shù)時將其刪除;不能在這之后通過聲明一個函數(shù)然后不再使用的方式來將其刪除。
struct widget
{
// deleted operator new prevents widget from being dynamically allocated.
void* operator new(std::size_t) = delete;
};
刪除普通成員函數(shù)或非成員函數(shù)可阻止有問題的類型提升導(dǎo)致調(diào)用意外函數(shù)。這可發(fā)揮作用的原因是,已刪除的函數(shù)仍參與重載決策,并提供比提升類型之后可能調(diào)用的函數(shù)更好的匹配。函數(shù)調(diào)用將解析為更具體的但可刪除的函數(shù),并會導(dǎo)致編譯器錯誤。
// deleted overload prevents call through type promotion of float to double from succeeding.
void call_with_true_double_only(float) =delete;
void call_with_true_double_only(double param) { return; }
請注意,在前面的示例中,使用 call_with_true_double_only 參數(shù)調(diào)用 float 將導(dǎo)致編譯器錯誤,但使用 call_with_true_double_only 參數(shù)調(diào)用 int 不會導(dǎo)致編譯器錯誤;在 int 示例中,此參數(shù)將從 int 提升到 double,并成功調(diào)用函數(shù)的 double 版本,即使這可能并不是預(yù)期目的。若要確保使用非雙精度參數(shù)對此函數(shù)進行的任何調(diào)用均會導(dǎo)致編譯器錯誤,您可以聲明已刪除的函數(shù)的模板版本。
template < typename T >
void call_with_true_double_only(T) =delete; //prevent call through type promotion of any T to double from succeeding.
void call_with_true_double_only(double param) { return; } // also define for const double, double&, etc. as needed.
上一篇:深入解析C++編程中基類與基類的繼承的相關(guān)知識
欄 目:C語言
本文標題:詳解在C++中顯式默認設(shè)置的函數(shù)和已刪除的函數(shù)的方法
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/2547.html
您可能感興趣的文章
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言沒有round函數(shù) round c語言
- 04-02C語言中怎么打出三角函數(shù) c語言中怎么打出三角函數(shù)的值
- 01-10求子數(shù)組最大和的解決方法詳解
- 01-10深入二叉樹兩個結(jié)點的最低共同父結(jié)點的詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計- 解析最少換車次數(shù)的問題詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計-用棧實現(xiàn)表達式求值的方法詳解
- 01-10HDOJ 1443 約瑟夫環(huán)的最新應(yīng)用分析詳解
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10使用C++實現(xiàn)全排列算法的方法詳解


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 01-10C#中split用法實例總結(jié)
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 04-02jquery與jsp,用jquery
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-11ajax實現(xiàn)頁面的局部加載


