隐式类型转换
在C++中存在着一把双刃剑—隐式类型转型。好处是可以编写过多的构造函数,坏处是总会有一些意外情况并非程序员所愿。
场景1
下面分别实现了有无被explicit
关键字修饰的类和函数。
#include <iostream> using std::cout; using std::endl; class Rational1 { public: Rational1(int n = 0, int d = 1):num(n), den(d) { cout << __func__ << "(" << num << "/" << den << ")" << endl; } public: int num; // 被除数 int den; // 除数 }; class Rational2 { public: explicit Rational2(int n = 0, int d = 1) :num(n), den(d) { cout << __func__ << "(" << num << "/" << den << ")" << endl; } public: int num; // 被除数 int den; // 除数 }; void Display1(Rational1 r) { cout << __func__ << endl; } void Display2(Rational2 r) { cout << __func__ << endl; } int main() { Rational1 r1 = 11; Rational1 r2(11); Rational2 r3 = 11; // error E0415 Rational2 r4(11); Display1(1); Display2(2); // error E0415 return 0; }
运行报错:这是因为Rationl2这个类没有对应int参数的构造函数。
在这里,并没有体现出explicit的优点,甚至觉得有它使得代码更加麻烦。但是我们必须要阻止那种带有歧义的隐式类型转换。
场景2
这里的例子来自C++11提案,但有一些细微的变动。定义了一个Ptr
类,并实现了bool
类型转换。这样我们就可以很方便使用if(p)
来判断指针是否有效。但是带来的坏处就是支持Ptr对象的相加运算,例如 p + pd
。
#include <iostream> using std::cout; using std::endl; template<class T> class Ptr { public: Ptr(T* p) :m_p(p) {} operator bool() const { return m_p != nullptr ? true : false; } private: T* m_p; }; int main() { int a = 11; Ptr<int> p(&a); if(p) { cout << "valid pointer." << endl; // 有效的指针 } else { cout << "invalid pointer." << endl; // 无效的指针 } Ptr<int> pd(0); cout << p + pd << endl; cout << pd << endl; return 0; }
运行结果:
valid pointer. 1 0
很明显这并不是我们想要的结果。这时我们只需要使用explicit
关键字修饰到bool类型转换上就可以有效的防止这种”意外“发生。
在bool运算符重载中添加explicit
运行结果:
没有找到接受’Ptr(int>'类型左操作数的操作符(或者没有可接受的转换)。
场景3
这里定义了一个ConvertTo和Convertable类。在Convertable类中有一个显式转换到ConverTo类的方法。
#include <iostream> using std::cout; using std::endl; class ConvertTo{}; class Convertabkle { public: explicit operator ConvertTo() const { return ConvertTo(); } }; void Func(ConvertTo ct) { } int main() { Convertabkle c; ConvertTo ct(c); ConvertTo ct2 = c; // 拷贝构造函数 error ConvertTo ct3 = static_cast<ConvertTo>(c); // C++11的强制类型转换 Func(c); // 拷贝构造函数 error return 0; }
那么这样就会使得只有直接初始化或者使用强制类型转换才可以进行转换,不能再通过拷贝构造函数隐式转换了。
总结
1、从上面几个例子中,可以看出显示类型转换explicit关键字并没有完全禁止从源类型到目标类型的转换,只是不允许拷贝构造函数和隐式类型转换罢了。所以我们就不能通过复制发方式或者通过函数参数的形式进行从源类型到目标类型的转换。
2、所以我们有必要在创建类时及时添加explicit关键字,禁止隐式类型转换。