1.构造函数转换和explicit关键字
在学习构造函数转换之前,先来复习下什么是自动类型转换:将一个标准类型的变量赋值给另一个标准类型的变量时,如果这两种类型兼容,则C++自动会将这个值转换为接收变量的值。
MyClass(int i);//声明一个只有一个参数int的构造函数
Myclass mc;//通过默认参数获取mc对象
mc = 2;//这是可以的,通过构造函数转换完成了由int->MyClass的自动转换
#pragma once
const double M2K = 1.6;
//定义一个类,表示距离,并分别用km和miles表示
class Distance
{
private :
int m_km;
double m_miles;
public:
Distance();//无参构造
Distance(int km);//只有一个int值的构造
Distance(double mile);//只有一个double值的构造
void show_by_km();//使用km输出
void show_by_mile();//使用mile输出
~Distance();//析构函数
};
Distance.cpp中实现类:
#include "distance.h"
#include <iostream>
Distance::Distance()
{
m_km = m_miles = 0;
}
Distance::Distance(int km)
{
m_km = km;
m_miles = km * M2K;
}
Distance::Distance(double mile)
{
m_miles = mile;
m_km = (int)m_miles / M2K;
}
void Distance::show_by_km()
{
std::cout << "distance:" << m_km <<" km" << std::endl;
}
void Distance::show_by_mile()
{
std::cout << "distance:" << m_miles << " mile(s)" << std::endl;
}
Distance::~Distance()
{
}
main.cpp中:
#include <iostream>
#include "distance.h"
using namespace std;
int main()
{ //使用无参构造初始化Distance对象
Distance d;
d = 23.4;//自动类型转换,通过Distance(double)构造函数
d.show_by_mile();
d.show_by_km();
return 1;
}
运行:
如果在声明构造方法时加上explicit关键字,则会关闭自动转换,但可以进行强制转换:
explicit Distance(int km);
//explicit关闭Distance和double自动转换特性
explicit Distance(double mile);
main.cpp中:
Distance d;
//由于使用了explicit关键字,只能进行强制转换
d = (Distance)23.4;
那么编译器在什么时候调用构造转换函数进行自动转换呢?以上例为例,他会在什么时候调用Distance(int)或者Distance(double)呢?如果使用了关键字explicit,那么只会在强制类型转换时调用;如果没有使用explicit关键字,则有四种情况下会使用构造转换函数:
1.使用构造转换函数初始化时:Distance d(1.3);
2.将double值赋值给Distance时:Distance d; d = 1.3;
3.将double值传递给接收Distance对象或引用的函数时:
4.函数的返回值为Distance类型,但返回double值时:
Distance getDistance(double s)
{
return s;
}
对于第四点需要注意,如果使用getDistance(double)传入参数为long类型,那么如果只存在Distance(double),那么他会先将long自动转换为double,然后将double自动转换为Distance;如果还存在Distance(int),那么编译器将无法确定是先将long转换为double呢还是int呢,因此存在二义性。2.转换函数
到这里为止,掌握了通过构造转换函数(一个参数的构造函数)实现了参数类型转换为类,那么,类能否转换为参数类型呢?答案是可以的,但是不再是通过构造转换函数进行转换,构造转换函数只适用于从某种类型转换为类类型,要进行相反的转换,通过转换函数进行实现。
2.1.转换函数的创建
创建转换函数形式如下:
operator typeName();
创建转换函数的规则:
1.转换函数必须是类的成员函数;
2.转换函数不能指定返回类型,但有返回值;
3.转换函数没有参数;
例如,如果要将Distance d转换为double类型,则可以在Distance中定义如下的转换函数:
operator double();
下面还是以Distance为例,实现Distance转double:
示例2.转换函数的使用
Distance.h中定义转换函数:
operator double();
在Distance.cpp中编写转换函数:
Distance::operator double()
{
return m_miles;
}
main.cpp中:
int main()
{
Distance distance;
distance = 23.4;
distance.show_by_mile();
distance.show_by_km();
//使用转换函数进行转换
double temp = distance;
cout << "temp:" <<temp<< endl;
//这里没有重载<<操作符,为啥可以这样输出?
cout << "distance:" << distance << endl;
return 1;
}
当调用double temp = d时编译器检测到类型不匹配,然后编译器会查看是否定义了与此匹配的转换函数,如果有定义,则进行转换,没有定义则编译失败。
在上例中,并没有重载<<操作符,但是为什么在cout<<distance时可以输出呢?这里还是调用到了转换函数,在执行这句是,编译器发现不匹配,因此会首先寻找是否存在的匹配函数,结果发现了operator double(),因此将distance隐式地转换为double输出了。
现在我再定义一个转换函数,用于将Distance转换为int,如下:
Distance.h中:
operator int();
Distance.cpp中:
Distance::operator int()
{
//四舍五入
return (int)(m_miles + 0.5);
}
然后继续运行,结果发现编译失败了:
系统提示“有多个运算符<<”,原来是出现二义性了,因为当编译器查看是否有转换函数时,发现有两个相关类型的转换函数,不确定要转换为哪一个,因此出现二义性。
要解决这个问题,可以进行显式的转换:
cout << "distance:" << (double)distance << endl;
和构造转换函数一样,转换函数也可以通过explicit关键字来取消其自动转换的特性,而是每次都通过显式的转换来进行转换,避免在用户不需要类型转换时就自动进行转换。
explicit operator double();
explicit operator int();
2.2.转换函数的优缺点
利用转换函数可以自动执行隐式转换,但是也正是这个原因,当用户不需要转换时,也会自动进行转换。因此,应该谨慎使用隐式自动转换,因尽量通过关键字explicit取消自动转换,使用显式类型转换。