<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近在閱讀android底層原始碼的時候,發現其中好多程式碼使用了explicit關鍵字,因此這裡對explicit關鍵字進行了分析和介紹。
在要求隱式轉換的程式上下文中,我們可以通過將建構函式宣告為explicit加以組織:
class Sales_data { public: Sales_data() = default; Sales_data(const std::string &s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p*n) {} explicit Sales_data(const std::string &s): bookNo(s) {} explicit Sales_data(std::istream&); Sales_data& combine(const Sales_data &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue;; return *this; } private: double avg_price() const {return units_sold ? revenue / units_sold : 0; } string bookNo; unsigned units_sold = 0; double revenue = 0.0; };
此時,沒有任何建構函式能用於隱式地建立Sales_data物件:下面的兩種用法都無法通過編譯:
Sales_data item; // right, 呼叫預設建構函式 Sales_data item2("book"); // right, 呼叫explicit Sales_data(const std::string &s): bookNo(s) {} item.combine(null_book); // error: string建構函式式explicit的 item.combine(cin); // error: istream建構函式式explicit的
關鍵字 explicit 只對一個實參的建構函式有效。需要多個實參的建構函式不能用於執行隱式轉換,所以無須將這些建構函式指定為 explicit 的。只能在類內宣告建構函式時使用 explicit 關鍵字,在類外部定義時不應重複:
// error: explicit 關鍵字只允許出現在類內的建構函式宣告處 explicit Sales_data::Sales_data(istream& is) { read(is, *this); }
發生隱式轉換的一種情況時當我們執行拷貝的初始化時(使用 = )。此時,我們只能使用直接初始化而不能使用explicit建構函式:
Sales_data null_book("book", 1, 10.0); // right Sales_data item1(null_book); // right,直接初始化 Sales_data item2 = null_book; // error, 不能將explicit 建構函式用於拷貝形式的初始化過程
儘管編譯器不會將 explicit 的建構函式用於隱式轉換過程,但是我們可以使用這樣的建構函式顯式地強制進行轉換:
Sales_data null_book("book", 1, 10.0); // right // right: 直接初始化 item.combine(Sales_data(null_book)); // right: static_cast可以使用explicit的建構函式 item.combine(static_cast<Sales_data>(cin));
在第一個呼叫中,我們直接使用Sales_data的建構函式,該呼叫通過接受string建構函式建立了一個臨時的 Sales_data 物件。在第二個呼叫中,我們使用 static_cast 執行了顯式的而非隱式的轉換。其中 static_cast 使用 istram 的建構函式建立了一個臨時的Sales_data物件。
《C++ prime》第五版,14.9.1中關於型別轉換的介紹:
在實踐中,類很少提供型別轉換運運算元。在大多數情況下,如果型別轉換自動發生,使用者可能會感覺比較意外,而不是感覺受到了幫助。然而這條經驗法則存在一種例外情況:對於類來說,定義向bool的型別轉換還是比較普遍的現象。
在C++標準的早期版本中,如果類想定義一個向bool的型別轉換,則它常常遇到一個問題:因為bool是一種算術型別,所以類型別的物件轉換成bool後就能被用在任何需要算數型別的上下文中。這樣的型別轉換可能引發意想不到的結果,特別是當istream含有向bool的型別轉換時,下面的程式碼仍將通過編譯:
int i = 42; cin << i; // 如果向bool的型別轉換不是顯式的,則該程式碼在編譯器看來將是合法的! // 這個程式只有在輸入數位的時候,i會預設為整數,輸入字串則會為0
這段程式檢視將輸出運運算元用作輸入流。因為istream本身並沒有定義<<,所以本來程式碼應該產生錯誤。然而,該程式碼能使用istream的bool型別轉換運運算元將cin轉換成bool,而這個bool值接著會被提升成int並用作內建的左移運運算元的左側運算物件。這樣一來,提升後的bool值(1或0)最終會被左移42個位置。這一結果顯示與我們的預期大相徑庭。
為了防止這樣的異常情況發生,C++11新標準引入了顯式的型別轉換運運算元(explicit conversion operator):
class SmallInt { public: // 編譯器不會自動執行這一型別轉換 explicit operator int() const {return val;} // 其他成員與之前的版本一致 };
和顯示的建構函式一樣,編譯器(通常)也不會將一個顯式的型別轉換運運算元用於隱式型別轉換:
SmallInt si = 3; // 正確:SmallInt的建構函式不是顯式的 si + 3; // 錯誤:此處需要隱式的型別轉換,但類的運運算元是顯式的 static_cast<int>(si) + 3; // 正確:顯示地請求型別轉換。這裡的static_cast<int>可以進行強制型別轉換
當型別轉換運運算元是顯式的時,我們也能執行型別轉換,不過必須通過顯式的強制型別轉換才可以。
該規定存在一個例外,即如果表示式被用作條件,則編譯器會將顯式的型別轉換自動應用於它。換句話說,當表示式出現在下列位置時,顯式的型別轉換將被隱式地執行:
// explicit關鍵字的作用就是防止類建構函式的隱式自動轉換 // 並且explicit關鍵字只對有一個引數的類建構函式有效,如果類建構函式引數大於 // 或等於兩個時,是不會產生隱式轉換的,所有explicit關鍵字也就無效了。 #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> using namespace std; class CxString // 這裡沒有使用explicit關鍵字的類宣告,即預設為隱式宣告 { public: char *_pstr; int _size; CxString(int size) { cout << "CxString(int size), size = " << size << endl; _size = size; // string的預設大小 _pstr = (char*)malloc(size + 1); memset(_pstr, 0, size + 1); } CxString(const char *p) { int size = strlen(p); _pstr = (char*)malloc(size + 1); // 分配string的記憶體 strcpy(_pstr, p); _size = strlen(_pstr); cout << "CxString(const char *p), strlen(p) = " << size << endl; } ~CxString() { if (_pstr != nullptr) { delete(_pstr); _pstr = nullptr; } } }; int main() { CxString string1(24); // right, 為CxString預分配24位元組的大小的記憶體 CxString string2 = 10; // right, 為CxString預分配10位元組的大小的記憶體 CxString string3; // error, 因為沒有預設建構函式, 錯誤為: 「CxString」: 沒有合適的預設建構函式可用 CxString string4("aaaa"); // right CxString string5 = "bbb"; // right, 呼叫的是CxString(const char *p) CxString string6 = 'c'; // right, 其實呼叫的是CxString(int size), 且size等於'c'的ascii碼 string1 = 2; // right, 為CxString預分配2位元組的大小的記憶體 string2 = 3; // right, 為CxString預分配3位元組的大小的記憶體 CxString string3 = string1; // right, 至少編譯是沒問題的, 但是如果解構函式裡用free釋放_pstr記憶體指標的時候可能會報錯, 完整的程式碼必須過載運運算元"=", 並在其中處理記憶體釋放 return 0; }
上面的程式碼中, “CxString string2 = 10;” 這句為什麼是可以的呢? 在C++中, 如果的建構函式只有一個引數時, 那麼在編譯的時候就會有一個預設的轉換操作:將該建構函式對應資料型別的資料轉換為該類物件. 也就是說 “CxString string2 = 10;” 這段程式碼, 編譯器自動將整型轉換為CxString類物件, 實際上等同於下面的操作:
CxString string2(10); 或 CxString temp(10); CxString string2 = temp;
但是, 上面的程式碼中的_size代表的是字串記憶體分配的大小, 那麼呼叫的第二句 “CxString string2 = 10;” 和第六句 “CxString string6 = ‘c’;” 就顯得不倫不類, 而且容易讓人疑惑. 有什麼辦法阻止這種用法呢? 答案就是使用explicit關鍵字. 我們把上面的程式碼修改一下, 如5.2小節。
// explicit關鍵字的作用就是防止類建構函式的隱式自動轉換 // 並且explicit關鍵字只對有一個引數的類建構函式有效,如果類建構函式引數大於 // 或等於兩個時,是不會產生隱式轉換的,所有explicit關鍵字也就無效了。 #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> using namespace std; class CxString // 這裡沒有使用explicit關鍵字的類宣告,即預設為隱式宣告 { public: char *_pstr; int _size; int _age; explicit CxString(int size) { cout << "CxString(int size), size = " << size << endl; _size = size; // string的預設大小 _pstr = (char*)malloc(size + 1); memset(_pstr, 0, size + 1); } CxString(const char *p) { int size = strlen(p); _pstr = (char*)malloc(size + 1); // 分配string的記憶體 strcpy(_pstr, p); _size = strlen(_pstr); cout << "CxString(const char *p), strlen(p) = " << size << endl; } // 上面也已經說過了, explicit關鍵字只對有一個引數的類建構函式有效。 // 如果類建構函式引數大於或等於兩個時, 是不會產生隱式轉換的, 所以explicit關鍵字也就無效了. explicit CxString(int age, int size) { _age = age; _size = size; } ~CxString() { if (_pstr != nullptr) { delete(_pstr); _pstr = nullptr; } } }; int main() { CxString string1(24); // right, 為CxString預分配24位元組的大小的記憶體 CxString string2 = 10; // error, 因為取消了隱式轉換 CxString string3; // error, 因為沒有預設建構函式, 錯誤為: 「CxString」: 沒有合適的預設建構函式可用 CxString string4("aaaa"); // right CxString string5 = "bbb"; // right, 呼叫的是CxString(const char *p) CxString string6 = 'c'; // error, 其實呼叫的是CxString(int size), 且size等於'c'的ascii碼, 因為取消了隱式轉換 string1 = 2; // error, 因為取消了隱式轉換 string2 = 3; // error, 因為取消了隱式轉換 CxString string3 = string1; // right, 至少編譯是沒問題的, 但是如果解構函式裡用free釋放_pstr記憶體指標的時候可能會報錯, 完整的程式碼必須過載運運算元"=", 並在其中處理記憶體釋放 return 0; }
但是, 也有一個例外, 就是當除了第一個引數以外的其他引數都有預設值的時候, explicit關鍵字依然有效, 此時, 當呼叫建構函式時只傳入一個引數, 等效於只有一個引數的類建構函式,
例子如下:
class CxString // 使用關鍵字explicit宣告 { public: int _age; int _size; // 此時該建構函式等效於只有一個引數的類建構函式,explicit可以生效 explicit CxString(int age, int size = 0) { _age = age; _size = size; // 程式碼同上, 省略... } CxString(const char *p) { // 程式碼同上, 省略... } }; // 下面是呼叫: CxString string1(24); // right CxString string2 = 10; // error, 因為explicit關鍵字取消了隱式轉換 CxString string3; // error, 因為沒有預設建構函式 string1 = 2; // error, 因為取消了隱式轉換 string2 = 3; // error, 因為取消了隱式轉換 string3 = string1; // error, 因為取消了隱式轉換, 除非類實現操作符"="的過載
到此這篇關於C++中的explicit關鍵字詳解的文章就介紹到這了,更多相關C++ explicit 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45