C++继承了C的类型转换的特性,C++允许编译器在不同类型之间执行隐式转换。
比如char转化为int,int转换为double,有从高向低转换(数据缺失),也有从低向高转换;不管怎样,这些都是C++允许的,相对安全的。
对于自定义的类型,其隐式类型可以通过单一自变量的构造函数和隐式类型转换操作符来实现。
指能够以单一变量成功调用的构造函数,该构造函数可能只有一个参数,也可能有多个参数,并且除了第一个参数的其他参数都有默认值。
举个栗子:
class Name
{
public:
Name(const string& s)//可以把string 转换为name
{
...
}
}
举个栗子:
#include
using namespace std;
class Retional
{
private:
int numerator;
int denominator;
public:
Retional(int x,int y)
{
numerator=x;
denominator=y;
}
operator double() const//将 Retional 转换为 double
{
return numerator * 1.0 / denominator;
}
};
int main()
{
Retional r(1,2);
cout<<r<<endl;//0.5
double x=0.5*r;//隐式类型转换函数在这种情况下会被调用,很隐秘,将Retional类型转化为了double类型
cout<<x<<endl;//0.25
}
隐式类型转换乍一看没有什么问题,但是存在你不想转换的时候,他也帮你转换了;而且结果不一定是正确的,这种你无法控制的行为,很难对其进行调试。
解决方案:
提供一个功能对等的显式函数来取代隐式类型转换函数,通过显示调用该函数来完成类型转换。显式地调用让转换更加可控。
举个栗子:
#include
using namespace std;
class Retional
{
private:
int numerator;
int denominator;
public:
Retional(int x,int y)
{
numerator=x;
denominator=y;
}
double toDouble() const //显式 类型转换函数
{
return numerator*1.0/denominator;
}
};
int main()
{
Retional r(1,2);
//cout<
cout<<r.toDouble()<<endl;//0.5 显式的调用转换函数比隐式的类型转换函数更加可靠
double x=0.5*r.toDouble();
cout<<x<<endl;//0.25
}
再举一个更加典型的栗子:
template<class T>
class Array{
public:
Array(int lowbound,int highbound);
Array(int size);
T& operator[] (int index);
...
};
bool operator==(const Array<int> &lhs,const Array<int> &rhs);
int main(){
Array<int> a(10);
Array<int> b(10);
for(int i = 0;i <10;i++)
{
if(a==b[i]){
//do something
}
else{
//do something
}
}
return 0;
}
应该是a[i]==b[I],但是此时编译器并没有报错!它会通过Array(int size)将b[i]隐式地转换为Array,每次迭代都会用a的内容和这个数组进行比较,所以他没有实现比较数组中每个元素的功能,而且效率低下。
面对这种问题有2种解决办法,你希望提供一个单自变量的constructors给客户使用,与此同时你也希望阻止编译器不分青红皂白地调用这样的constructors。
方法一:采用expilcit关键字,禁止编译器对该关键字修饰的函数进行隐式类型转换
举个栗子:
#include
using namespace std;
template<class T>
class Array
{
public:
Array(int lowbound,int highbound)
{
}
explicit Array(int size)
{
}
T& operator [](int index)
{
}
};
bool operator==(const Array<int> &lhs,const Array<int> &rhs)
{
}
int main()
{
Array<int> a(10);
Array<int> b(10);
for(int i=0; i<10; i++)
{
//if(a==b[i]){} //error 加了explicit无法隐式转换
if(a==Array<int>(b[i]))//可行,调用显示构造函数
{
}
if(a==static_cast<Array<int> >(b[i]))//可行,调用C++类型转换函数
{
}
if(a==(Array<int>)(b[i]))//可行,C的旧式转型
{
}
}
return 0;
}
方法二:采用嵌套类
C++中存在这样一条规则:没有任何一个转换程序可以内含一个以上的“用户定制转换行为(即单自变量的构造函数和隐式类型转换符)”,也就是说,必要的时候编译器可以先进行内置类型之间的转换再调用带单自变量的构造函数或者先调用隐式类型转换符再进行内置类型的转换,但不可能连续进行两次用户定制的转换行为。
前置知识:嵌套类
#include
class Car{
public:
//外部公共类接口
void startEngine(){
//使用嵌套类封装启动引擎的实现细节
Engine engine;
engine.activate();
}
private:
// 嵌套类,封装启动引擎的实现细节
class Engine{
public:
void activate(){
std::cout << "Engine activated." << std::endl;
//具体的启动引擎的细节
}
};
};
int main(){
Car myCar;
myCar.startEngine();
return 0;
}
输出
Engine activated.
#include
using namespace std;
template<class T>
class Array
{
public:
class ArraySize //内部代理类
{
private:
int thesize;
public:
ArraySize(int numElements):thesize(numElements){}
int size() const
{
return thesize;
}
};
Array(int lowbound,int highbound)
{
}
explicit Array(ArraySize size)//使用内部代理类进行参数声明
{
}
T& operator [](int index)
{
}
};
bool operator==(const Array<int> &lhs,const Array<int> &rhs)
{
}
int main()
{
Array<int> a(10);
Array<int> b(10);
for(int i=0; i<10; i++)
{
if(a==b[i])//因为内部代理类的存在,所以编译无法通过
{
}
}
return 0;
}
通过使用内部代理类,不但可以以一个整数作为构造函数的自变量来指定数组的大小,又能阻止一个整数被隐式的类型转换未一个临时的Array对象!
避免隐式类型转换函数被调用的三种方式:
允许编译器执行隐式类型转换,害处将多过好处,所以不要提供这种隐式的类型转换,除非你真的真的很需要!
书山有路勤为径,学海无涯苦作舟。
3.1《More Effective C++》