C++ explicit顯式關(guān)鍵字的實現(xiàn)
一、explicit 關(guān)鍵字的核心定義
explicit 是C++中的修飾符關(guān)鍵字,唯一的作用場景是修飾類的構(gòu)造函數(shù),它的核心功能是:禁止編譯器對被修飾的構(gòu)造函數(shù)執(zhí)行「隱式類型轉(zhuǎn)換 / 隱式構(gòu)造」行為。
二、前置知識:什么是「隱式類型轉(zhuǎn)換/隱式構(gòu)造」?
要理解explicit,必須先理解它要禁止的行為是什么。
觸發(fā)隱式構(gòu)造的前提
一個類的構(gòu)造函數(shù)滿足以下條件時,編譯器就具備了「隱式轉(zhuǎn)換」的能力:
? 構(gòu)造函數(shù)是單參數(shù)構(gòu)造函數(shù)(只有1個入?yún)ⅲ?br />? 或,多參數(shù)構(gòu)造函數(shù),但除第一個參數(shù)外,其余參數(shù)都有默認(rèn)值(本質(zhì)等價于「可單參數(shù)調(diào)用」的構(gòu)造函數(shù))。
隱式構(gòu)造的本質(zhì)
編譯器會自動將「單個入?yún)⒌闹怠罐D(zhuǎn)換為「當(dāng)前類的臨時對象」,這個轉(zhuǎn)換過程是編譯器偷偷完成的,不需要程序員手動寫構(gòu)造代碼,所以叫「隱式」。
三、無 explicit 時:隱式構(gòu)造生效(反面示例)
下面的代碼是不加explicit的情況,可以直觀看到隱式構(gòu)造的效果,這也是explicit要解決的場景:
#include <iostream>
using namespace std;
class Test {
public:
int num;
// 單參數(shù)構(gòu)造函數(shù):無explicit修飾,支持隱式構(gòu)造
Test(int n) : num(n) {
cout << "構(gòu)造函數(shù)執(zhí)行: num = " << num << endl;
}
};
// 測試函數(shù):入?yún)門est類型對象
void printTest(Test t) {
cout << "printTest: " << t.num << endl;
}
int main() {
// 場景1:直接賦值的隱式轉(zhuǎn)換
Test t1 = 10; // ? 編譯通過!編譯器自動把 10 → Test(10) 臨時對象 → 賦值給t1
cout << "t1.num = " << t1.num << endl;
// 場景2:函數(shù)傳參的隱式轉(zhuǎn)換
printTest(20); // ? 編譯通過!編譯器自動把 20 → Test(20) 臨時對象 → 傳給函數(shù)
return 0;
}
運行結(jié)果
構(gòu)造函數(shù)執(zhí)行: num = 10
t1.num = 10
構(gòu)造函數(shù)執(zhí)行: num = 20
printTest: 20
? 結(jié)論:無explicit時,編譯器幫我們完成了 整型值 → Test對象 的隱式轉(zhuǎn)換,代碼能編譯運行,但這種「自動轉(zhuǎn)換」往往是風(fēng)險來源。
四、加 explicit 時:隱式構(gòu)造被禁止(正面示例)
給上述代碼的構(gòu)造函數(shù)加上explicit修飾,代碼如下,所有隱式轉(zhuǎn)換的寫法都會直接編譯報錯:
#include <iostream>
using namespace std;
class Test {
public:
int num;
// 單參數(shù)構(gòu)造函數(shù):加explicit修飾,禁止隱式構(gòu)造
explicit Test(int n) : num(n) {
cout << "構(gòu)造函數(shù)執(zhí)行: num = " << num << endl;
}
};
void printTest(Test t) {
cout << "printTest: " << t.num << endl;
}
int main() {
// 場景1:直接賦值的隱式轉(zhuǎn)換
Test t1 = 10; // ? 編譯報錯!explicit禁止了這種隱式轉(zhuǎn)換寫法
// 場景2:函數(shù)傳參的隱式轉(zhuǎn)換
printTest(20); // ? 編譯報錯!explicit禁止了這種隱式轉(zhuǎn)換寫法
return 0;
}
報錯原因
編譯器提示類似:cannot convert 'int' to 'Test' in initialization,核心就是:explicit讓編譯器失去了「自動轉(zhuǎn)換類型」的權(quán)限。
五、加 explicit 后,正確的寫法:顯式構(gòu)造
?? 重要結(jié)論:explicit 只禁止隱式構(gòu)造,完全不影響「顯式構(gòu)造」!
被explicit修飾的構(gòu)造函數(shù),依然可以正常使用,只是必須手動顯式調(diào)用構(gòu)造函數(shù),這也是C++推薦的「安全寫法」,修改上述main函數(shù)的正確代碼:
int main() {
// 正確寫法1:標(biāo)準(zhǔn)顯式構(gòu)造(最常用)
Test t1(10);
cout << "t1.num = " << t1.num << endl;
// 正確寫法2:C++11列表初始化(同樣屬于顯式構(gòu)造)
Test t2{20};
cout << "t2.num = " << t2.num << endl;
// 正確寫法3:函數(shù)傳參時顯式構(gòu)造
printTest(Test(30));
printTest(Test{40});
return 0;
}
運行結(jié)果
構(gòu)造函數(shù)執(zhí)行: num = 10
t1.num = 10
構(gòu)造函數(shù)執(zhí)行: num = 20
t2.num = 20
構(gòu)造函數(shù)執(zhí)行: num = 30
printTest: 30
構(gòu)造函數(shù)執(zhí)行: num = 40
printTest: 40
? 結(jié)論:顯式構(gòu)造的寫法完全不受explicit影響,且邏輯清晰,可讀性更高。
六、explicit 的核心注意事項
? 注意1:explicit 只對「可單參數(shù)調(diào)用的構(gòu)造函數(shù)」有效
explicit的修飾對以下構(gòu)造函數(shù)無意義(加了也不會報錯,但屬于多余寫法):
- 無參構(gòu)造函數(shù)(
Test()); - 真正的多參數(shù)構(gòu)造函數(shù)(無默認(rèn)值,比如
Test(int a, int b));
因為這兩種構(gòu)造函數(shù)本身就無法觸發(fā)隱式構(gòu)造,編譯器沒有轉(zhuǎn)換的依據(jù)。
? 注意2:多參數(shù)+默認(rèn)值的構(gòu)造函數(shù),也需要加 explicit
這是最容易被忽略的坑!比如下面的構(gòu)造函數(shù),本質(zhì)是「可單參數(shù)調(diào)用」,不加explicit依然會觸發(fā)隱式構(gòu)造:
class Test {
public:
int a, b;
// 多參數(shù),但第二個參數(shù)有默認(rèn)值 → 等價于「可單參數(shù)調(diào)用」
Test(int x, int y = 0) : a(x), b(y) {}
};
int main() {
Test t = 100; // ? 編譯通過!隱式構(gòu)造:100 → Test(100, 0)
return 0;
}
? 建議:這種構(gòu)造函數(shù)必須加explicit,寫法如下:
explicit Test(int x, int y = 0) : a(x), b(y) {}
? 注意3:C++11擴(kuò)展:explicit 也可以修飾「轉(zhuǎn)換運算符」
C++11標(biāo)準(zhǔn)中,explicit的作用范圍被擴(kuò)大了:除了修飾構(gòu)造函數(shù),還可以修飾類的轉(zhuǎn)換運算符(operator 類型名),作用依然是:禁止自定義類型到其他類型的隱式轉(zhuǎn)換。
示例:
class Test {
public:
int num = 10;
// 轉(zhuǎn)換運算符:將Test對象轉(zhuǎn)為int類型
explicit operator int() const {
return num;
}
};
int main() {
Test t;
int a = t; // ? 編譯報錯!禁止隱式轉(zhuǎn)換 Test → int
int b = static_cast<int>(t); // ? 正確:顯式轉(zhuǎn)換,不受影響
return 0;
}
? 注意4:為什么要禁止隱式構(gòu)造?—— 核心價值
explicit不是語法糖,而是C++的安全機(jī)制,它的設(shè)計初衷是:避免「意外的隱式轉(zhuǎn)換」導(dǎo)致的邏輯錯誤和難以排查的bug。
- 隱式轉(zhuǎn)換是編譯器的「自動行為」,程序員很容易忽略這個轉(zhuǎn)換過程,導(dǎo)致代碼邏輯和預(yù)期不符;
- 隱式轉(zhuǎn)換會生成臨時對象,可能帶來不必要的性能開銷(雖然現(xiàn)代編譯器會優(yōu)化,但依然不推薦);
- 顯式構(gòu)造的代碼可讀性更高,誰看都知道這里是「創(chuàng)建了一個對象」,沒有歧義。
七、最佳實踐(行業(yè)通用規(guī)范)
所有滿足「可單參數(shù)調(diào)用」的構(gòu)造函數(shù),都建議加上 explicit 修飾!
除非你有明確的業(yè)務(wù)需求需要用到隱式構(gòu)造(這種場景極少,比如std::string的構(gòu)造函數(shù)允許const char*隱式轉(zhuǎn)為string,是為了兼容C語言的字符串寫法),否則一律加上explicit,這是C++開發(fā)的「行業(yè)最佳實踐」,也是大廠面試的高頻考點。
? 核心知識點總結(jié)(精華濃縮)
explicit是修飾符,僅用于修飾類的構(gòu)造函數(shù)(C++11可修飾轉(zhuǎn)換運算符);- 核心作用:禁止編譯器的隱式類型轉(zhuǎn)換/隱式構(gòu)造;
- 生效前提:構(gòu)造函數(shù)是「單參數(shù)」或「多參數(shù)+其余參數(shù)有默認(rèn)值」;
- 加
explicit后,只能用顯式構(gòu)造(Test t(n)/Test t{n}),隱式寫法(Test t = n)編譯報錯; explicit不影響顯式構(gòu)造,是C++的安全機(jī)制,推薦無腦加;- 本質(zhì)區(qū)別:隱式是「編譯器自動轉(zhuǎn)」,顯式是「程序員手動寫」,顯式代碼更安全、可讀性更高。
到此這篇關(guān)于C++ explicit顯式關(guān)鍵字的實現(xiàn)的文章就介紹到這了,更多相關(guān)C++ explicit顯式關(guān)鍵字內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解C語言中的wait()函數(shù)和waitpid()函數(shù)
這篇文章主要介紹了C語言中的wait()函數(shù)和waitpid()函數(shù),注意其在中斷進(jìn)程方面用法的不同,需要的朋友可以參考下2015-08-08
C++多重繼承引發(fā)的重復(fù)調(diào)用問題與解決方法
這篇文章主要介紹了C++多重繼承引發(fā)的重復(fù)調(diào)用問題與解決方法,結(jié)合具體實例形式分析了C++多重調(diào)用中的重復(fù)調(diào)用問題及相應(yīng)的解決方法,需要的朋友可以參考下2018-05-05

