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 只能出现在构造函数的声明中。







猜你喜欢

转载自blog.csdn.net/u011074149/article/details/80396881