类的自动转化和强制类型转换(C++)

可以将类定义成与基本类型或另一个类相关,使得从一种类型转换为另一种类型是有意义的。
当一个类的构造函数中,有只有接受一个参数的构造函数,这个构造函数就能作为转换函数。

#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》

猜你喜欢

转载自blog.csdn.net/MoooLi/article/details/82777861
今日推荐