现在的位置: 首页 > 综合 > 正文

转换函数 与 explicit

2014年02月08日 ⁄ 综合 ⁄ 共 1699字 ⁄ 字号 评论关闭

转换函数的基本规则:
        转换函数只能是成员函数,无返回值,空参数。
        不能定义到void的转换,也不允许转换成数组或者函数类型。
        转换常定义为const形式,原因是它并不改变数据成员的值。

 

 

转换函数所引出的问题:
        转换操作符过于强大,它可以定义到一种内置类型的转换,然而这种内置类型本身是可能继续转换成其他的内置类型的(不能转换到一个自定义类型后再转换到另一个自定义类型),如 operator int() const 定义了到int型的转换,然后int型可以转换到float、double、long等类型, 这样极不容易确定编译器为我们做了什么,在出现问题后,也极不容易定位错误。
        当类类型定义了一个到基本类型A的转换后,他可能在实际中转换到另一基本类型B,这时如果再定义一个到基本类型C的转换,而C也可以转换到B。这样,就会在实际运用中产生二义性,如A为int,B为long double,C为double,在产生自定义类型的对象时,需要转换,就会使得编译器并不知道如何处理,到底是调用哪个转换。

         使用自定义转换时,有两个潜在的缺陷:1.定义转换多,会像上述那样引发二义性;2.一些转换利大于弊。避免前者的方法是,最好在定义到内置类型的转换时,只定义一个(内置类型之间的转换太灵活了)。避免后者的除了经验和反复思考没什么好办法。
         《More Effective C++》建议将转换函数设计成一种asType()的函数形式,就像std::String中的成员函数c_str一样,提供一个函数,而不是一个转换。

 

转换函数引出的扩展问题:
        构造函数也会在标准类型到类类型的转换中起到意想不到的负面影响。当在某处需要一个类对象时,他会隐式调用构造函数完成转换,而当已经存在一个单参数构造函数接受了一种内置类型完成构造,又定义了一个单参数构造函数接受不同内置类型完成构造时,也会出现和转换函数相同的问题——二义性。
        构造函数和转换函数之间也可能存在二义性。如:

class Integer
{
public:
       Integer(int i = 0);
       operator int () const;
       friend Integer operator + (const Integer& i1, const Integer& i2);
private:
       int m_ivalue;
};

Integer A1,A2;
Integer A3 = A1 + A2;
int i = A3 + 1;   //
error
        这时,int i = A3 + 1;该如何做?是把A3转化成int做+运算?还是把1转换成Integer做operator+运算?
        如果定义了到算术内置操作符的转换,就不要在定义接受算术类型的操作符的重载版本。如果用户需要使用这些操作符,则转换操作将转换你所定义的类型对象,然后可以使用内置操作符。
        解决这个冲突的一个比较简单的方法是把Integer的构造函数声明成显示的explicit
        另一种比较复杂的方法是在Integer内部做一个proxy class代理类,把Integer的构造函数的参数声明成这个代理类而不是int。然后让这个代理类做一个构造函数接受一个int的类型。这样在需要A类型的隐式转换时,要先用int的值构造一个代理类型,然后再由Integer构造函数构造一个对象完成转换,这是不被允许的,两层的用户自定义类型间的转换编译器不支持,所以可以起到避免隐式转换的作用。
        (关于proxy class,可以看《More Effective C++》的条款30,那里有精彩的介绍)


explicit

        阻止不应该允许的经过转换构造函数进行的隐式转换的发生。声明为explicit的构造函数不能在隐式转换中使用。

        在带一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参数构造函数)原型中使用explicit将禁止进行隐式转换,但仍允许显示转换。

抱歉!评论已关闭.