= delete
delete的由來
如之前提到的,在沒有聲明默認特殊成員函數的時候,編譯器會自動幫我們補充,但有時候我們並不希望存在這些函數,比如:我們不希望某個類通過拷貝的方式實例化一個新的對象。就算我們不定義拷貝構造函數和重載拷貝賦值運算符,編譯器也會為我們自動完成。
#include <bits/stdc++.h>
using namespace std;
class LiF {
public:
LiF() = default;
LiF(int _lif) {
lif = _lif;
}
void print(){cout << lif << endl;}
~LiF() = default;
private:
int lif;
};
int main() {
LiF* lif1 = new LiF{2};
LiF lif2 = *lif1; // 我們不希望這個類可以拷貝構造
lif2.print();
delete lif1;
lif1 = nullptr;
return 0;
}
寫出如上代碼之后,編譯可以通過,但違反了我們的初衷。
再看下面這個場景:
double add(double x, double y) {
return x + y;
}
int main() {
int a, b;
add(a, b);
return 0;
}
這里我們聲明了double
類型的add
函數,編譯可以通過。假設此時我們不希望其他類型能通過隱式轉換調用這個函數,那這里就需要禁用掉會發生轉換的版本。
由此C++11引入了delete
關鍵字,用於顯式禁用某些函數。
delete示例
與default
不同的是,delete
沒有限制函數必須是類的特殊成員函數。
#include <bits/stdc++.h>
using namespace std;
class LiF {
public:
LiF() = default;
LiF(int _lif) {
lif = _lif;
}
LiF(const LiF& l) = delete; // 顯式禁用拷貝構造函數
LiF& operator= (const LiF& l) = delete; // 顯式禁用拷貝賦值運算符
void print(){cout << lif << endl;}
~LiF() = default;
private:
int lif;
};
int main() {
LiF* lif1 = new LiF{2};
LiF lif2 = *lif1; // 這里將引發報錯,因為拷貝賦值運算符已被顯式禁用
lif2.print();
delete lif1;
lif1 = nullptr;
return 0;
}
double add(double x, double y) {
return x + y;
}
int add(int, int) = delete; // 顯式禁用add函數的int版本
int main() {
int a, b;
add(a, b); // 這里將引發報錯,因為對應的函數已被顯式禁用
return 0;
}
這里有一點需要注意的是,delete
關鍵字僅僅禁用了函數的調用,但在編譯過程中,名字查找和重載解析時,該函數名仍是一個有效的標識符。
分析第二個例子:我們聲明並定義了double
類型的add
函數,聲明並顯式禁用了add
函數的int
重載。在編譯到add(a, b)
時,編譯器進行名字查找,找到全局作用域的add
函數定義,並通過精確匹配最終定位到int add(int, int)
這個重載上,隨后編譯器發現這是一個deleted(已被禁用)函數,引發報錯。