C++ explicit 關鍵字詳解

2021-03-02 CPP開發者

(給CPP開發者加星標,提升C/C++技能)

blog.csdn.net/l2563898960/article/details/97769569【導讀】:如何防止C++構造函數的隱式轉換,explicit關鍵字可以幫助我們解決問題。

以下是正文

explicit關鍵字的作用

explicit關鍵字在寫程序時使用的次數較少,但是仔細觀察會發現,在C++標準庫中的相關類聲明中explicit出現的頻率是很高的,那麼explicit關鍵字到底有什麼作用呢?接下來我就為大家一一解答。


explicit為清晰的;明確的之意.顧名思義,關鍵字explicit可以阻止隱式轉換的發生。

例如: C++中只帶有一個參數的構造函數,或者或者除了第一個參數外其餘參數都有預設值的多參構造函數,承擔了兩個角色:


1.用於構建單參數的類對象。


2.隱含的類型轉換操作符。

例如:一個類A的構造函數A(int i)就是,既可以用來作為構造器,又可以實現隱式轉換A a=1;因為1可以通過構造函數A(int i)轉換為一個類A的對象。(隱含的類型轉換操作符)

但有時我們並不想讓他進行隱式類型轉換,這時C++的explicit關鍵字就起到作用了。

注意:當類的聲明和定義分別在兩個文件中時,explicit只能寫在在聲明中,不能寫在定義中。


下面我將為大家介紹三種使用explicit關鍵字的情況:

類型轉換函數
#include<iostream>using namespace std;class Fraction{public:  Fraction(int numerator, int denominator = 1): m_numerator(numerator), m_denominator(denominator){}  operator double() const{    return (double)m_numerator/m_denominator;  }private:  int m_numerator;  int m_denominator;}
int main(void){ Fraction fraction(3, 5); double d = 3.5 + f; cout << d << endl; return 0;}

我們設計了一個Fraction類(分數類), 在主函數中定義了一個分數對象f,然後將3.5 + f賦值給double類型變量d, 但是我們發現f並不是一個double類型的變量,因此編譯器會從Fraction類中尋找operator double()函數,隱式調用該函數將Fraction類型轉換成一個double類型. operator double()就是我們所說的類型轉換函數(type conversion function)。

類型轉換函數的一般形式

1.轉換函數必須是類的成員函數


2.轉換函數不能聲明返回類型


3.形參列表必須為空


4.類型轉換函數通常應該是const

當我們想要在明確聲明類型轉換的時候,才使用類型轉化函數時,這時我們就需要使用到explicit關鍵字了.使用方法如下:

#include<iostream>using namespace std;class Fraction{public:  Fraction(int numerator, int denominator = 1): m_numerator(numerator), m_denominator(denominator){}  explicit operator double() const{    return (double)m_numerator/m_denominator;  }private:  int m_numerator;  int m_denominator;}
int main(void){ Fraction fraction(3, 5); double d = 3.5 + static_cast<double>(f); cout << d << endl; return 0;}

注意,這時當我們想調用類型轉換函數的時候,需要寫成static_cast<double>(f);

注意static_cast 是C++11 引入的類型轉換運算符。

單操作數構造函數

還是採用上面的Fraction類,這次我們重載(overload) 「+」 號運算符,使得仍然可以達到相同的效果。

#include<iostream>using namespace stdclass Fraction{ public:   Fraction(int numerator, int denominator = 1): m_numerator(numerator), m_denominator(denominator){}   double operator+(const Fraction& a)   {     return (a.m_numerator + this->m_numerator)/(a.denominator + this->m_denominator);   } private:   int m_numerator;  int m_denominator; }  int main(void){  Fraction fraction(3, 5);  double d = f + 3;  cout << d << endl;  return 0;}

在double d = f + 3 這句話中構造函數就是前面所提到的第二種角色隱含的類型轉換操作符。因為執行到這句話首先會調用+的重載函數,該函數的調用對象默認為左操作數,右操作數為Fraction類型,因此會調用構造函數將3轉換成Fraction類型,然後將得到的返回值double類型賦值給變量d。


同理如果不想讓構造函數進行隱式類型轉換,可以在構造函數前面加上explicit關鍵字,防止進行隱式轉換.使用方法如下:

#include<iostream>using namespace stdclass Fraction{ public:   explicit Fraction(int numerator, int denominator = 1): m_numerator(numerator), m_denominator(denominator){}   double operator+(const Fraction& a)   {     return (a.m_numerator + this->m_numerator)/(a.denominator + this->m_denominator);   } private:   int m_numerator;  int m_denominator; }  int main(void){  Fraction fraction(3, 5);  double d = f + 3;  cout << d << endl;  return 0;}

你可能會注意到,加上explicit 關鍵字之後,這個代碼將不能正確執行。

同時出現拷貝構造函數和類型轉換函數

我們看如下一段代碼:

#include<iostream>using namespace stdclass Fraction{ public:   Fraction(int numerator, int denominator = 1): m_numerator(numerator), m_denominator(denominator){}   operator int(){     return m_numerator/denominator;   }   int operator+(const Fraction& a)   {     return (a.m_numerator + this->m_numerator)/(a.denominator + this->m_denominator);   } private:   int m_numerator;  int m_denominator; }  int main(void){  Fraction fraction(3, 5);  int d = f + 3;  cout << d << endl;  return 0;}

這時你會發現會產生一個二義性問題,在執行int d = f + 3的時候到底是該選擇類型轉換函數,將f轉換成int類型再繼續運算呢?還是應該將3作為構造函數的參數進行隱式轉換,然後再調用+運算符重載函數呢?


解決這個問題的辦法就是使用explicit關鍵字限制,具體方法有兩種:


1.在構造函數前面加上explicit關鍵字, 防止int類型隱式轉換成為Fraction類型。


2.在類型轉換函數前面加上explicit關鍵字,這樣只有顯示調用類型轉換static_cast<int>(f)時,才會調用該函數。

拷貝構造函數

Copy constructor也是同樣的,如果Copy constructor被聲明為explicit,則這個類對象不能隱式調用,用於傳參傳遞和函數返回值。

Complex<double> v1(1.2, 2.3);  Complex<double> v2 = v1;        
void func(Complex<double> v); func(v1);
Complex<double> func() { Complex<double> v1(2.3, 1.2); return v1; }

總結

C++中,一個參數的構造函數(或者除了第一個參數外其餘參數都有預設值的多參構造函數),承擔了兩個角色。

用於構建單參數的類對象。

隱含的類型轉換操作符。

explicit關鍵字只對有一個參數的類構造函數有效, 如果類構造函數參數大於或等於兩個時, 是不會產生隱式轉換的, 所以explicit關鍵字也就無效了。

聲明為explicit的構造函數不能在隱式轉換中使用,只能顯示調用,去構造一個類對象。

Base base(『a』) Base base = 『a』 

儘量避免有二義性的類型轉換,如果類中包含一個或多個隱式類型轉換,則必需使用explicit關鍵字確保在類類型和目標類型之間只存在唯一一種隱式轉換方式,否則將出現二義性。

但是將拷貝構造函數聲明成explicit並不是良好的設計,一般只將有單個參數的constructor聲明為explicit,而copy constructor不要聲明為explicit。

- EOF -

關於 C++ 中explicit關鍵字詳解,歡迎在評論中和我探討。覺得文章不錯,請點讚和在看支持我繼續分享好文。謝謝!

關注『CPP開發者』

看精選C++技術文章 . 加C++開發者專屬圈子

↓↓↓

點讚和在看就是最大的支持❤️

相關焦點

  • Explicit,一個容易被忽視的關鍵字
    答案就是使用explicit關鍵字.可以參考文末的代碼理解上面也已經說過了, explicit關鍵字只對有一個參數的類構造函數有效, 如果類構造函數參數大於或等於兩個時, 是不會產生隱式轉換的, 所以explicit關鍵字也就無效了.
  • C++的轉換手段並與explicit關鍵詞配合使用
    >++作者:良知猶存轉載授權以及圍觀:歡迎添加微信公眾號:羽林君隱式轉化c++類型不匹配,reinterpret_cast可以讓編譯器以你的方法去看待它們:funcPtrArrayfuncPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething); //不同函數指針類型之間進行轉換explicit關鍵字(顯示的類型轉化運算符)
  • C++ typeid關鍵字詳解
    以下是正文typeid關鍵字注意:typeid是操作符,不是函數。這點與sizeof類似)。運行時獲知變量類型名稱,可以使用。需要注意不是所有編譯器都輸出」int」、」float」等之類的名稱,對於這類的編譯器可以這樣使用。
  • C++使用explicit關鍵詞的理解
    struct String {     explicit String(int size)     {    } };// String str = 1; // 編譯錯誤  上面介紹了explicit的使用,大概意思就是限制用戶的某一行為
  • c++11新特性,所有知識點都在這了!
    auto & decltype關於C++11新特性,最先提到的肯定是類型推導,C++11引入了auto和decltype關鍵字,使用他們可以在編譯期就推導出變量或者表達式的類型,方便開發者編碼也簡化了代碼。
  • C++ 20中的explicit(bool)介紹
    一句話開頭explicit(bool)是C++ 20中的一個特性,這個特性用於檢查通用類型的實現並且可以減少編譯時間。細說explicit(bool)在C++中,通過將對象封裝成其他類型的技法十分常見,例如std::pair和std::optional就是兩個十分典型的例子。並且,在C++標準庫,Boost或者你自己的代碼庫中,我們還可以看到許多類似的使用。
  • C/C++中常用的編程關鍵字
    關鍵字作用:關鍵字是C++中預先保留的單詞(標識符)在定義變量或者常量時候,不要用關鍵字C++關鍵字如下:19. explicitexplicit(顯式的)的作用是"禁止單參數構造函數"被用於自動型別轉換,其中比較典型的例子就是容器類型。在這種類型的構造函數中你可以將初始長度作為參數傳遞給構造函數。
  • C++ initializer_list 詳解
    有了initializer_list之後,對於STL的container的初始化就方便多了,比如以前初始化一個vector需要這樣:std::vector v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);而現在c++
  • C++、java 和 C 的區別
    一、基礎類型c++:** java:** C#:1.以java為準,c++裡面的int short long 像這樣的整型 一般都有unsigned 和signed的區分 ,這個跟java和c# 的區別比較大,但c#裡面有unit ulong ushort 這三種就相當於c++的修飾詞unsigned,當c++李明的變量類型定義unsigned,就默認是整數。
  • 九大程式語言優缺點第四期:c++
    C++:c++誕生於1983年,緊隨c語言的步伐,c++是C語言的超集,大家所知道的C語言是面向過程的,java是面向對象的,那麼C語言為了面向對象,所以誕生出現在大家所熟知的c++,被廣泛視為大規模應用構建軟體。
  • C++ vector詳解
    以下是正文前言本文mark了vector的一些接口,介紹了vector中的對內存和對象的管理詳解請見cppreference-vector。gt; fourth (third); int myints[] = {16,2,77,29}; std::vector<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );列表初始化可以使用c++
  • 那些容易犯錯的c++保留字
    本文首發 | 公眾號:lunvey目前正在學習vc++6.0開發,而這裡面使用的是c++98標準。
  • json for modern c++的使用
    json for modern c++是一款非常好用的json庫,具有語法直觀和使用簡單的特點,並且是用C++11標準編寫的,此外還支持STL和json容器之間的轉換,可謂集方便又強大。本文推薦給廣大C++程式設計師,相信學習完本文之後,在處理json時一定會得心應手。
  • C++typename的由來和用法
    這個關鍵字用於指出模板聲明(或定義)中的非獨立名稱(dependent names)是類型名,而非變量名。我們經常會這麼用 typename,這是一項C++程式語言的泛型編程(或曰「模板編程」)的功能,typename關鍵字用於引入一個模板參數。
  • C++中的static關鍵字的總結
    最近在備C++的課程,所以把在備課過程中遇到的問題進行了總結和梳理,先把C++中的static關鍵字的用法做以下總結。C++的static有兩種用法:面向過程程序設計中的static和面向對象程序設計中的static。前者應用於普通變量和函數,不涉及類;後者主要說明static在類中的作用。
  • C++ 的幾個for 循環,範圍for語句
    例子:#include <iostream>using namespace std;int main(){ string str("this is a c++"); //每行輸出str中的一個字符 for(
  • c++之內存分配、命名空間、強制類型轉換學習總結
    一、C++動態內存分配:在學習c語言的時候,我們一般都是使用庫函數malloc()來進行內存的申請分配,然後使用庫函數free()來進行釋放申請到的內存;現在在c++裡面採用了另外一種內存申請的方法:c++中通過
  • 「含蓄的」是「implicit」還是「explicit」?
    但這些詞卻讓眾多剛接觸英語或是正在努力學習英語的小夥伴們很是頭疼~今天我們就一起來學習一組容易混淆的單詞「implicit」和「explicit」的不同以及用法~This article is explicit and suitable for beginners.這篇文章很簡單易懂,很適合初學者。
  • C++11特性:decltype關鍵字
    而編譯時類型推導,除了我們說過的auto關鍵字,還有本文的decltype。decltype與auto關鍵字一樣,用於進行編譯時類型推導,不過它與auto還是有一些區別的。decltype的類型推導並不是像auto一樣是從變量聲明的初始化表達式獲得變量的類型,而是總是以一個普通表達式作為參數,返回該表達式的類型,而且decltype並不會對表達式進行求值。
  • C++之字符串類學習總結
    一、回顧c語言對字符串的實現:一般我們在c語言要實現對字符串操作的話,一般是採用字符數組或者一組函數來實現的,為啥這樣做呢,那是因為c語言裡面根本就沒有字符串類型的關鍵字;而且c語言也支持自定義類型,所以更加無法獲得字符串類型為了解決這個問題,在c++中,引入了自定義類型,而且可以通過類來完成對字符串類型的定義。