1. 程式人生 > >C++隱式型別轉換建構函式和關鍵字explicit

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 只能出現在建構函式的宣告中。