在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.
順序 選擇 循環 總結
