可以将类定义成与基本类型或另一个类相关,使得从一种类型转换为另一种类型是有意义的。
当一个类的构造函数中,有只有接受一个参数的构造函数,这个构造函数就能作为转换函数。
#pragma once
#ifndef STONEWT_H_
#define STONEWT_H_
class Stonewt
{
private:
enum { Lbs_per_stn = 14 };
int stone;
double pds_left;
double pounds;
public:
Stonewt();
Stonewt(double lbs);
Stonewt(int stn, double lbs);
virtual ~Stonewt();
void show_lbs() const;
void show_stn() const;
};
Stonewt::Stonewt()
{
}
Stonewt::Stonewt(double lbs)
{
stone = int(lbs) / Lbs_per_stn;
pds_left = int(lbs) % Lbs_per_stn+lbs-int(lbs);
pounds = lbs;
}
Stonewt::Stonewt(int stn, double lbs)
{
stone = stn;
pds_left = lbs;
pounds = stn * Lbs_per_stn + lbs;
}
Stonewt::~Stonewt()
{
}
#endif // !STONEWT_H_
其中,Stonewt(double lbs)这个构造函数可以作为转换函数,即我们可以这样:
Stonewt myCat;
myCat = 19.6;
即为
Stonewt myCat = Stonewt(19.6);
然而,如果给第二个参数提供默认值,它便可用于转换int
Stonewt(int stn, double lbs = 0);
将构造函数用作自动类型转换函数似乎是一项不错的特性。然而,当程序员拥有更丰富的C++经验时,将发现这种自动特性并非总是合乎需要的,因为这会导致意外的类型转换。因此,C++新增了关键字explicit,用于关闭这种自动特性:
explicit Stonewt(double lbs);
这将关闭上述示例中介绍的隐式转换,但仍然允许显式转换,即显式强制转换。
然而,当且仅当转换不存在二义性时,才会进行这种二步转换,也就是说,如果这个类还定义了构造函数Stonewt(long lbs),则编译器将拒绝这些语句,可能指出,int可被转换为long或double,因此调用存在二义性。
转换函数:
既然可以将数字转换成Stonewt对象,那么可以做相反的转换吗?也就是说,是否可以将Stonewt对象转换为double值,就像如下所示:
Stonewt wolfe(285.7);
double host = wolfe;
可以这样做,但不是使用构造函数,构造函数只用于从某种类型转换到类类型的转换。要进行相反的转换,必须使用特殊的C++运算符函数——转换函数。
转换函数是用户定义的强制类型转换,可以像使用强制类型转换那样使用它们。例如,如果定义了从Stonewt到double的转换函数,就可以使用下面的转换:
Stonewt wolfe(285.7);
double host = double(wolfe);
double thinker = (double)wolfe;
也可以让编译器来决定如何做:
Stonewt(20, 3);
double star = wells;
编译器发现,右侧是Stonewt类型,而左侧是double类型,因此它将查看程序员是否定义了与此匹配的转换函数。(如果没有找到这样的定义,编译器将生成错误消息,指出无法将Stonewt赋给double。)
要转换为typeName类型,需要使用这种形式的转换函数:
operator typeName();
注意:
- 转换函数必须是类方法;
- 转换函数不能指定返回类型;
- 转换函数不能有参数。
例如,转换为double类型的函数的原型如下:
operator double();
typeName指出了要转换成的类型,因此不需要指定返回类型。转换函数是类方法意味着:它需要通过类对象来调用,从而告知函数要转换的值。因此,函数不需要参数。
Stonewt.h
#pragma once
#ifndef STONEWT_H_
#define STONEWT_H_
#include<iostream>
class Stonewt
{
private:
enum { Lbs_per_stn = 14 };
int stone;
double pds_left;
double pounds;
public:
Stonewt();
Stonewt(double lbs);
Stonewt(int stn, double lbs);
virtual ~Stonewt();
void show_lbs() const;
void show_stn() const;
operator int() const;
operator double() const;
};
Stonewt::Stonewt()
{
}
Stonewt::Stonewt(double lbs)
{
stone = int(lbs) / Lbs_per_stn;
pds_left = int(lbs) % Lbs_per_stn+lbs-int(lbs);
pounds = lbs;
}
Stonewt::Stonewt(int stn, double lbs)
{
stone = stn;
pds_left = lbs;
pounds = stn * Lbs_per_stn + lbs;
}
Stonewt::~Stonewt()
{
}
void Stonewt::show_lbs() const {
std::cout << pounds <<" pounds\n";
}
void Stonewt::show_stn() const {
std::cout << stone << " stone, " << pds_left << " pounds\n";
}
Stonewt::operator int() const {
return int(pounds + 0.5);
}
Stonewt::operator double() const {
return pounds;
}
#endif // !STONEWT_H_
假设有以下代码:
Stone.cpp
#include<iostream>
#include"Stonewt.h"
int main() {
using std::cout;
Stonewt poppins(9, 2.8);
double p_wt = poppins;
cout << "Convert to double => ";
cout << "Poppins: " << p_wt << "pounds.\n";
cout << "Convert to int => ";
cout << "Poppins: " << (int)poppins << "pounds.\n";
return 0;
}
下面是程序清单的输出:
如果将cout << "Poppins: " << (int)poppins << "pounds.\n"
的(int)去掉,因为在cout示例中,并没有指出应转换为int还是double类型,因此不可行,程序中出现了二义性。
但如果类只定义了double转换函数,则编译器将接受该语句。这是因为只有一种转换可能,因此不存在二义性。
赋值的情况与此类似。对于当前的类声明来说,编译器将认为下面的语句有二义性而拒绝它:
long gone = poppins;
在C++中,int和double值都可以被赋给long变量,所以编译器使用任意一个转换函数都是合法的。
当类定义了两种或更多的转换时,仍可以用显式强制类型转换来指出要使用哪个转换函数。
和转换构造函数一样,转换函数也有其优缺点。提供执行自动、隐式转换的函数所存在的问题是:在用户不希望进行转换时,转换函数也可能进行转换。因此,最好使用显式转换,而避免隐式转换。在C++11中,可用explicit将转换运算符声明为显式的。
声明:以上整理自个人理解和Stephen Prata 著的《C++ Primer Plus》