C++ 类的自动转换和强制类型装换
0X00 前言
在C++中,类的设计是一件十分重要的事情,一个类设计的好坏取决于它接口是否优良,即函数的设计
有时候我们可能需要设计多种多样的接口,目的是为了增强类的多态性。
今天我们来讲一讲类的转化问题
在此之前,我们来定义一个类,作为我们后面的
class Dog
{
private:
int GoOutTimes; //表示一周遛狗的次数
double Kilo; //表示一周遛狗走的公里数
double Time; //表示一周遛狗的时间
public:
Dog(double ti); //构造函数:初始化遛狗时间
Dog(int times,double ki); //构造函数:初始化遛狗次数与遛狗距离
Dog(); //默认构造函数
~Dog(); //析构函数
void show_Ki() const; //展示公里数
void show_ti() const; //展示时间
};
0X10 初始化化时的自动转换
1、利用构造函数的隐式转换
可以看到,上面的类提供了三个构造函数:
Dog(double ti);
Dog(int times,double ki);
Dog();
我们可以在声明类对象的时候就调用构造函数进行初始化:
Dog Chichi(12.6);
Dog Mimi(3,10.2);
Dog Lolo();
因为一个Dog对象表示一个狗狗的一周外出情况(天数,公里数以及时间),那么我们是否可以提供一些将整数或者浮点数转化为Dog对象的方法
由于我们拥有对应的构造函数:
Dog(double ti);
因此我们就可以利用构造函数将double类型转化为 Dog 类型。是不是有点不可思议?
然而它的确是可以工作的
Dog myDog;
myDog=14.3;
当程序运行到 myDog=14.3;
时,编译器会去检查构造函数中是否有 Dog(double)
这样的构造函数,既然存在,那么就用 Dog(double)
来创建一个 临时的 Dog 对象,然后采用逐个成员赋值的方式将该临时对象复制到 myDog 中, 这一过程叫做 隐式转换 ,因为这是自动进行的。
有一点我们需要特别注意
只有接受一个参数的构造函数才能作为转换函数
也就是说 Dog(int times,double ki);
函数是不能用来进行转换的,除非它能给第二个参数提供默认值:
Dog(int times,double ki=8.9);
这时候才能用户转换 int
2、explicit 限定下的显式
但是有时为了保证安全性,又必须阻止这种不安全的行为发生。(是不是听起来比较矛盾,但这正是语言设计者需要考虑的事情)
也就是说关闭这种隐式转换
因此引进 explicit
(显式的) 限定词,在它的修饰下,表示对于修饰的函数,这样的隐式转换将不起作用:
explicit Dog(double ti);
此时,上面的 隐式转换将不起作用,但是依旧可以用显式转换来进行强制转换:
Dog myDog;
//error
myDog = 13.2
//right
myDog = Dog(13.2);
myDog = (Dog) 13.2;
3、小结
来总结一下刚才所讲的内容:
在使用了 explicit
关键字后,Dog (double)
只能用于 显式强制类型转换;
否则,还可以用于以下隐式转换:
-
将Dog 对象初始化为 double 值时
-
将double 值传递给 Dog对象时
-
将double值传递给接受Dog参数的函数时
-
返回值被声明为Dog的函数并试图返回double值时
-
在上述任意情况下,使用可转化为double类型的内置类型时
可能最后一点,大家不太明白。
我们来举个例,就拿最上面的类,因此存在Dog(double ti);
构造函数,所有我们知道,将double转化为 Dog类型是可以的:
Dog myDog;
myDog = 14.5;
然而其实下面的代码也是可以的:
Dog myDog;
myDog = 14;
其实也是很好理解的 因为 int (14)型是可以转化为 double(14.0) 型的,因此这样是合法的。
但是我们也应特别注意的是:二义性问题
什么意思呢?
假如我们Dog类中还有这样一个函数 Dog(long)
,那么 myDog=14
的转化将是无效的!
因为 int 既可以转化为 double 也可以转化为 long ,那么此时就出现了让编译器为难的 二义性问题。
那么编译器就罢工喽。
0X11 自定义转换函数
上面我们讲到了可以将 基本类型(如 double) 转换为我们的类对象,前提是有相对应的构造函数。
那么,能否直接将类对象转换为 基本类型 呢?(听起来又是个很奇特的想法),就像这样:
Dog Nana(7.6);
double Time=Nana;
答案是可以的!
但是,这时候我们需要的就不是构造函数,而是 一种特殊的运算符函数——转换函数
那么如何创建转换函数呢?
假设我们要转换为typeName类型,需要使用这种形式的转换函数:
operator ypename ()
注意以下几点:
-
转换函数必须是类方法;
-
转换函数不能指定返回类型;
-
转换函数不能有参数;
现在我们将 Dog 对象转换为 int 和 double 类型的函数添加到类的声明当中:
class Dog
{
private:
int GoOutTimes; //表示一周遛狗的次数
double Kilo; //表示一周遛狗走的公里数
double Time; //表示一周遛狗的时间
public:
Dog(double ti); //构造函数:初始化遛狗时间
Dog(int times,double ki); //构造函数:初始化遛狗次数与遛狗距离
Dog(); //默认构造函数
~Dog(); //析构函数
void show_Ki() const; //展示公里数
void show_ti() const; //展示时间
operator int() const; //转换为 int
operator double() const; //转换为 double
函数的实现内容则可以自己规定:
//将遛狗天数返回加1
Dog::operator int() const
{
return GoOutTimes+1;
}
//返回一周遛狗平均公里数
Dog::operator double() const
{
return Kilo/7;
}
0X20 后言
C++为了拓展类的多态性,提供了很多设计类的方法,如果我们能很好的运用,那么对我们的编码工作将是很好的帮助。
希望今天的内容能对大家有所帮助~
谢谢大家~
来日再见!
————————————————————————————————————————————————————————
参考:《C++ primer Plus 第6版》