2,More Effective C++——条款5(谨慎使用定制“类型转换函数”)

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类型

猜你喜欢

转载自blog.csdn.net/zhizifengxiang/article/details/82949510