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取消自动转换,使用显式类型转换。