C++隱式類型轉換構造函數和關鍵字explicit

轉自公衆號:碼農有道

1. 隱式類型轉換構造函數:

    在《C++ Primer》這本經典教程中提到:可以用單個實參來調用的構造函數定義從形參類型到該類類型的一個隱式轉換。這句話看起來比較繞口挺不好理解的。我們還是結合實例來理解。

#include <iostream>
using namespace std;

class Complex{
public:
    Complex(double r, double i=0)
        :m_real(r), m_imag(i){
        cout<<"constructor function:"<<endl;
        cout<<"m_real = "<<m_real<<" m_imag = "<<m_imag<<endl;
    }
    Complex(){
        cout<<"No-argument constructor function:"<<endl;
    }
    friend void print(Complex c);
private:
    double m_real;
    double m_imag;
};

void print(Complex c){
    cout<<"m_real = "<<c.m_real<<" m_imag = "<<c.m_imag<<endl;
}

int main(int argc, char *argv[])
{
    Complex c1 = 5;    //1
    Complex c2;
    c2 = 4;            //2
    print(10);         //3
    return 0;
}


上面的例子中,Compex(double r, double i = 0)這個構造函數就是前面所說的可以用單個實參來調用的構造函數 ,因爲它的第二個參數是默認參數。像這樣的函數也稱爲轉換構造函數

那什麼又是從形參類型到該類類型的一個隱式轉換了?在上例中,1,2,3處(截圖中已經標明)就發生了所謂的從形參類型到該類類型的一個隱式轉換 。

我們以第3處的函數調用print(10)爲例,由於print 函數定義時是接收一個Complex類型的參數,現在調用時傳了個10,由於轉換構造函數的存在,可以將10隱式轉換爲一個臨時Complex 對象傳遞過去

看到沒,通過10得到了Complex對象,這就是以轉換構造函數形參類型此處是double類型---10可以隱式轉換爲double類型)到該類類型 (這個臨時對象)隱式轉換 

到這裏,我相信大家應該明白了什麼是隱式轉換構造函數了吧。

2. 隱式轉換的隱患

    隱式類型轉換表面上看給我們帶來了很大的方便,但是實際上很多時候卻會給我們代碼埋下很深的隱患,看看下面的代碼。

#include <iostream>
using namespace std;

class MyString{
public:
    MyString(int size){} //構造函數
    MyString(const char* s=NULL){}//構造函數2
};

int main(){
    MyString s1 = 'a';//這裏原意是想用字符串"a"初始化s1,
                        //結果不小心將雙引號""打成單引號''
    return 0;
}

從上面可以看出,第14行原本我們是想寫 Mystring s1 = "a",通過構造函數2將s1初始化爲字符串"a"的,結果不小心將雙引號" "寫成了單引號' ',由於轉換構造函數1(功能是創建size大小的字符串)的存在,它會用'a'構造出一個臨時的Mystring 對象(含97(a的ascii碼)字節的字符串)來初始化s1。結果s1被初始化爲了含97字節的字符串。

上面那種情況並不是我們希望的,程序運行的時候,凡是用到s1的地方都可能出現邏輯錯誤,而這種錯誤又是很難定位的。

那麼有沒有一種辦法可以預防這種情況的發生?現在是該我們講述explicit 關鍵字的時候了。

3. explicit 關鍵字用法

    好了,經過前面那麼長鋪疊,現在終於輪到我們的主角explicit 登場了,前面說了隱式轉換常常會帶來程序邏輯的錯誤,而且這種錯誤一旦發生是很難察覺的,應當儘量避免。

    那麼怎麼避免這種隱式轉換 ,這就是關鍵字explicit的作用了,在聲明構造函數的時候前面添加上explicit即可,這樣就可以防止這種轉換。  C++中的explicit關鍵只需用於修飾只有一個參數的類構造函數, 它的作用是表明該構造函數是顯示的, 而非隱式的, 跟它相對應的另一個關鍵字是implicit, 意思是隱藏的,類構造函數默認情況下即聲明爲implicit(隱式)。

#include <iostream>
using namespace std;

class MyString{
public:
    explicit MyString(int size){} //構造函數
    MyString(const char* s=NULL){}//構造函數2
};

int main(){
    MyString s1 = 'a';//這裏原意是想用字符串"a"初始化s1,
                        //結果不小心將雙引號""打成單引號''
    return 0;
}

可以看出,當構造函數前加了explicit關鍵字後,原本代碼中發生隱式轉換的地方現在在編譯的時候不能通過,這樣,也就防止了我們程序中可能出現的問題,記住,編譯器是我們最好的朋友,我們儘可能的將代碼中的隱患錯誤暴露給編譯器,讓它提醒我們以便及時去糾正我們的錯誤。

需要注意的是,explicit 只能出現在構造函數的聲明中。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章