1 隐式类型转换
C++中允许如下3种形式的隐式类型转换:
1. 基本类型隐式类型转换:
int a = 10;
double b = a;
2. 单参数构造构造函数
class Name { // 可以将char* 类型转换成Name类型
Name(const char* str) {}
};
class Apple { // 可以将int类型转换成Apple类型
Apple(int a, double b = 10);
};
3,自定义隐式类型转换操作符:关键字operator后加上类型名称。该函数无返回类型。
class Banana {
Banana(int a, int b) {}
operator double() const; // 将Banana转换成double类型
}
Banana ba(1, 2);
double b = 2 + ba; // ba隐式转换成double类型
2 带来的问题
1 问题一
隐式类型转换会带来一些我们所不期望、或者想不到的副作用。比如下面情况。显然我们需要为Banana类重载一个“<<”操作符,但是由于Banana类可以隐式转换成double类型,即便不重载"<<"操作符,下面的输出语句也可以成功执行。
Banana ba(1, 2)
std::cout << ba;
因此,我们最后为Banana类,直接定义一个类型转换函数。每次需要Banana类的double类型时,直接调用该函数即可。相应地,STL中string类型也无法隐式转换成const char*类型。
double Banana::toDouble(){
}
2 问题二
下面代码展示了,单一构造函数导致类型转换的问题。由于List类定义了传入一个int类型的构造函数,因此b[index]作为一个int类型,可以转换成List类型,从而调用了List的重载函数:==。变成两个List对象进行比较。
template<class T> class List {
public:
List(int size) {}
T& operator[] (int index);
bool operator ==(const List<int>& lh, const List<int>& rh) ;
}
List<int> a(10), b(10);
if (a == b[0]) { // b[index]进行类型转换成了一个List类对象
// do somthing else
}
3 解决隐式类型转换带来的问题
有两种方式可以解决,上面提出的单一参数构造函数带来的隐式类型转换问题。
1 使用explicit关键字
C++引入了explicit修饰符,来修饰单一参数构造函数带来的隐式类型转换问题。explicit禁止隐式类型转换,但是允许显式类型转换。
template<class T> class List {
explicit List(int size) {}
......
}
List<int> a(10), b(10);
if (a == b[1]) ... // 非法,类型不匹配
if (a == List<int>(b[1])) ... // 合法,定义一个新List对象进行比较
if (a == static_cast<List<int> >(b[0]))...... // 合法,但是无意义
if (a == (List<int>)(b[0]))...... // 合法,但是无意义
2 使用内部类防止类型转换
C++不允许进行两次隐式类型转换,比如下面代码就是非法的:int转换成List,List又转换成Vector。(一次转换路径可以确认,对于两次转换路径,无法知道第二次转换应该走那一条路径)
class List {
public:
List(int size);
}
class Vector {
public:
Vector(const List& list);
}
List list(10);
Vector vector(list);
if (list == 10)..... // 合法,单一参数构造函数造成隐式类型转换
if (vector == 10) .... // 非法,连续两次类型转换
下面代码将List的长度"int size"封装到内部类型ListSize中。初始化时,int可隐式转换成ListSize类型,对List进行初始化。而a == b[1]进行比较时,由于int无法进行两次转换成List,因此编译错误。
template<class T> class List {
public:
class ListSize {
public:
ListSize(int size) : m_size(size){}
int size() {return m_size;}
private:
int m_size;
}
List(ListSize size); // 此处将自定义类型作为参数传入,从而避免基本类型进行隐式转换
};
bool operatr ==(const List<int>& lh, const List<int>& rh);
List<int> a(10), b(10); // 合法,int类型转换成ListSize, 用ListSize对List进行初始化
if (a == b[1]) .... // 非法,int类型只能转换成ListSize类型,不能转换成List类型