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

