转换函数 与 explicit

本文探讨了C++中转换函数的基本规则及其可能导致的问题,包括二义性和不易定位的错误。文章还讨论了如何通过设计asType函数或使用explicit关键字来避免这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转换函数的基本规则:
        转换函数只能是成员函数,无返回值,空参数。
        不能定义到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将禁止进行隐式转换,但仍允许显示转换。

### C++ 中 `explicit` 构造函数的作用及使用方法 在 C++ 编程语言中,`explicit` 是一个关键字,专门用于修饰类的构造函数,以防止编译器进行不必要的隐式类型转换。其主要目的是增强程序的类型安全性,从而避免由于隐式转换引发的潜在错误或意外行为。 #### 作用 `explicit` 关键字的主要作用是禁止编译器在某些上下文中自动调用单参数构造函数(或可以通过默认参数变为单参数的构造函数)进行隐式类型转换。例如,如果没有使用 `explicit`,编译器可能会将一个 `int` 类型的值自动转换为某个类的对象,这种隐式转换可能会导致难以调试的错误。 具体来说,当一个构造函数被标记为 `explicit` 时,它只能被显式地调用,而不能通过赋值或函数参数传递时的隐式转换来调用。这有助于提高代码的可读性和健壮性。 #### 使用方法 要使用 `explicit`,只需在构造函数的声明前加上该关键字。通常,`explicit` 用于只有一个参数的构造函数,或者具有多个参数但其余参数都有默认值的情况。 ##### 示例代码 ```cpp class MyClass { public: explicit MyClass(int d) : data(d) {} // 显式构造函数 int get_data() const { return data; } private: int data; }; int main() { MyClass obj1(10); // 正确:显式调用构造函数 // MyClass obj2 = 20; // 错误:由于 explicit,不允许隐式转换 MyClass obj3 = MyClass(30); // 正确:显式调用构造函数 return 0; } ``` 在这个例子中,`MyClass` 的构造函数被声明为 `explicit`,因此不能通过赋值操作符 `=` 来隐式地从 `int` 转换为 `MyClass` 对象。然而,显式的构造函数调用(如 `MyClass obj1(10)` 或 `MyClass obj3 = MyClass(30)`)仍然是允许的。 ##### 多参数构造函数 `explicit` 也可以应用于具有多个参数的构造函数,尤其是当这些参数中包含默认值时。这种情况下,如果构造函数被标记为 `explicit`,则必须显式调用构造函数,而不能通过隐式转换来调用。 ```cpp class Rectangle { public: explicit Rectangle(int width, int height = 10) : w(width), h(height) {} int area() const { return w * h; } private: int w, h; }; int main() { Rectangle r1(5, 20); // 正确:显式调用构造函数 // Rectangle r2 = 15; // 错误:由于 explicit,不允许隐式转换 Rectangle r3 = Rectangle(15); // 正确:显式调用构造函数,使用默认高度 10 return 0; } ``` 在这个例子中,`Rectangle` 类的构造函数接受两个参数,其中第二个参数有默认值。由于构造函数被标记为 `explicit`,因此不能通过隐式转换来调用构造函数,但可以显式调用。 #### 总结 `explicit` 关键字在 C++ 中用于防止构造函数的隐式调用,确保类型转换是显式的,从而提高代码的安全性和可维护性。合理使用 `explicit` 可以帮助开发者避免一些潜在的错误,并使代码更加清晰易懂。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值