C++11 =default 和 =delete


在C++中,聲明自定義的類型之后,編譯器會默認生成一些成員函數,這些函數被稱為默認函數。其中包括

(1)(默認)構造函數

(2)拷貝(復制)構造函數

(3)拷貝(復制)賦值運算符

(4)移動構造函數

(5)移動賦值運算符

(6)析構函數

另外,編譯器還會默認生成一些操作符函數,包括

(7)operator ,

(8)operator &

(9)operator &&

(10)operator *

(11)operator ->

(12)operator ->*

(13)operator new

(14)operator delete

【1】顯式缺省函數(=default)

如果類設計者又實現了這些函數的自定義版本后,編譯器就不會去生成默認版本。

大多數時候,我們需要聲明帶參數的構造函數,此時就不會生成默認構造函數,這樣會導致類不再是POD類型(可參見隨筆《C++11 POD類型》),從而影響類的優化:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Test 
 5 {
 6 public:
 7     Test(int i) : data(i) {}
 8 
 9 private:
10     int data;
11 };
12 
13 int main()
14 {
15     std::cout << std::is_pod<Test>::value << std::endl;  // 0
16 }

C++11中提供了新的機制來控制默認函數生成來避免這個問題:聲明時在函數末尾加上”= default”來顯式地指示編譯器去生成該函數的默認版本,這樣就保證了類還是POD類型:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Test 
 5 {
 6 public:
 7     Test() = default;  // 顯式指定缺省函數
 8     Test(int i) : data(i) {}
 9 
10 private:
11     int data;
12 };
13 
14 int main()
15 {
16     std::cout << std::is_pod<Test>::value << std::endl;  // 1
17 }

【2】顯式刪除函數(=delete)

另一方面,有時候可能需要限制一些默認函數的生成。

例如:需要禁止拷貝構造函數的使用。以前通過把拷貝構造函數聲明為private訪問權限,這樣一旦使用編譯器就會報錯。

而在C++11中,只要在函數的定義或者聲明后面加上”= delete”就能實現這樣的效果(相比較,這種方式不容易犯錯,且更容易理解):

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Test 
 5 {
 6 public:
 7     Test() = default;  // 顯式指定缺省函數
 8     Test(int i) : data(i) {}
 9     Test(const Test& t) = delete; // 顯式刪除拷貝構造函數
10 
11 private:
12     int data;
13 };
14 
15 int main()
16 {
17     Test objT1;
18 //    Test objT2(objT1); // 無法通過編譯
19 }

【3】其他應用

(1)= default和= delete 能夠更加精准的控制類的默認函數版本。其中,= default同樣也能在類外的定義中修飾成員函數:

 1 class Example 
 2 {
 3 public:
 4     Example() = default;
 5     Example(const Example&);
 6 
 7 private:
 8     int data;
 9 };
10 
11 Example::Example(const Example& ex) = default;

這樣的好處是,能夠為一個類實現多個版本,只要我們在頭文件里聲明同樣的函數,而在不同的cpp文件中用不同的方法實現,當選擇不同的cpp編譯時產生的版本就不同。

(2)關於= delete,它不僅局限於限制生成默認函數,還能避免編譯器做一些不必要的隱式數據轉換:

隱式轉換示例:

 1 class Example 
 2 {
 3 public:
 4     Example(int i) {}
 5 };
 6 
 7 
 8 int main()
 9 {
10     Example ex(1);
11     Example ex1('a');  // 編譯成功,char會隱式裝換成int型
12 }

有時候我們不想要這種隱式轉換:

 1 class Example 
 2 {
 3 public:
 4     Example(int i) {}
 5     Example(char c) = delete;
 6 };
 7 
 8 
 9 int main()
10 {
11     Example ex(1);
12//   Example ex1('a');  // 無法通過編譯
13 }

這個方法也能用於普通函數:

1 void func(int i) {}
2 void func(char c) = delete;
3 
4 int main()
5 {
6     func(1);
7//   func('a');   // 編譯失敗
8 }

另外,還有很多使用場景,例如,顯式刪除new操作符來避免在堆上分配對象、顯式刪除析構函數用於構建單例模式等等。

總之,default只能用於6個特殊成員函數,但delete可用於任何成員函數。

 

good good study, day day up.

順序 選擇 循環 總結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM