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

11.9 类的自动类型转换和强制类型转换

将一个标准类型变量的值赋给另一个标准类型的变量时,如果这两种类型兼容,则C++自动将这个值转换为接收变量的类型。

long count = 8; // int value 8 converted to type long
double time = 11; // int value 11 converted to type double
int side = 3.33; // double value 3.33 converted to type int 3

上述赋值语句都是可行的,因为C++看来,各种数值类型都表示相同的东西—一个数字,同时C++包含用于进行转换的内置规则。然而,这些转换将降低精度;而且,C++语言不自动转换不兼容的类型。

int * p = 10; // type clash --- 这个会执行失败,但是呢,允许使用强制类型转换

然而,当自动类型转换失败时,可以使用强制类型转换。

int * p = (int *) 10; // ok, p and (int *) 10 both pointers

11.9.1 使用构造函数实现隐式类型转换

针对下面这三个构造函数:

Stonewt(double lbs); // constructor for double pounds

可以使用以下方式初始化对象:

Stonewt myCat; // create a Stonewt object
myCat = 19.6; // use Stonewt(double) to convert 19.6 to Stonewt

程序将使用构造函数Stonewt(double)来创建一个临时的Stonewt对象, 并将19.6作为初始化值。随后,采用逐成员赋值方式将该临时对象的内容复制到myCat中。这一过程称为隐式转换,因为是自动进行的,而不需要显式强制类型转换。

只有接受一个参数的构造函数才能作为转换函数。

然而,如果给第二个参数提供默认值,他便可用于转换int:
Stonewt(int stn, double lbs = 0); // int-to-Stonewt conversion

什么时候使用构造函数隐式类型转换功能

  • 将Stonewt对象初始化为double值时;
  • 将double值赋给Stonewt对象时;
  • 将double值传递给接受Stonewt参数的函数时;
  • 返回值被声明为Stonewt的函数试图返回double值时;
  • 在上述任意一种情况下,使用可转换为double类型的内置类型时。

下面详细介绍最后一点,函数原型化提供的参数匹配过程,允许使用Stonewt(double)构造函数来转换其他数值类型。也就是说,下面两条语句都首先将int转换为double,然后使用Stonewt(double)构造函数。

Stonewt Jumbo(7000); // uses Stonewt(double), converting int to double
Jumbo = 7300; // uses Stonewt(double), converting int to double

然而,当且仅当转换不存在二义性时,才会进行这种二步转换。也就是说,如果这个类还定义了构造函数Stonewt(long)则编译器将拒绝这些语句,可能指出:int可被转换为long或double,因此调用存在二义性。

11.9.2 Explicit关键字关闭构造函数隐式类型转换功能—仅可使用显式转换功能

随着程序员拥有更丰富的C++经验,将会发现自动类型转换并非总是合乎需要的,因为这会导致意外的类型转换。因此,C++新增了关键字explicit,用于关闭自动类型转换,也就是说,可以这样声明构造函数:

explicit Stonewt(double lbs); // no implicit conversions allowed

这将关闭隐式转换,但仍然允许显式转换,即显式强制类型转换。

Stonewt myCat; // create a Stonewt object
myCat = 19.6; // not valid if Stonewt(double) is declared as explicit
mycat = Stonewt(19.6); // ok, an explicit conversion
mycat = (Stonewt) 19.6; // ok, old form for explicit typecast

11.9.3 转换函数

构造函数只用于从某种类型到类类型的转换。要进行相反的转换,必须使用特殊的C++运算符函数—转换函数。

转换函数是用户定义的强制类型转换,可以像使用强制类型转换那样使用它们。

11.9.3.1 转换函数格式

要转换为typeName类型,需要使用这种形式的转换函数:

operator typeName();

有三个注意事项:

  • 转换函数必须是类方法
  • 转换函数不能指定返回类型
  • 转换函数不能有参数

例如,转换为double类型的函数的原型如下:

operator double();

转换为int类型是采用四舍五入的方式。

11.9.3.2 使用转换函数

隐式转换

Poppins有两个转换函数,分别是转换为int和double类型。

double p_wt = poppins;

隐式转换缺点

  • 隐式转换将对成员函数参数进行转换,而不是调用成员函数的对象。
    long gone = poppins; // ambiguous
  • 在C++中,int和double值都可以被赋值给long变量,所以编译器使用任意一个转换函数都是合法的。编译器不想承担选择转换函数的责任。然而,如果删除了这两个转换函数之一,编译器将接受这条语句。例如,假设省略了double定义,则编译器将使用int转换,将poppins转换为一个int类型的值。然后再将它赋给gone时,将int类型转换为long类型。

针对上述含糊的情况,可以使用显式类型转换如下:

long gone = (double) poppins; // use double conversion
long gone = int (poppins); // use int conversion
隐式转换的函数所存在的问题是:在用户不希望进行转换时,转换函数也可能进行转换。

11.9.3.3 关闭转换函数隐式类型转换功能—仅可使用显式转换功能
11.9.3.3.1 方法1—explicit关键字
class Stonewt
{
    ...
    // conversion functions
    explicit operator int() const;
    explicit operator double() const;
};

11.9.3.3.2 方法2—成员函数
int Stonewt::Stone_to_Int() { return int (pounds + 0.5); }

原则上说,最好使用显式转换,而避免隐式转换。在C++98中,关键字explicit不能用于转换函数,但C++11消除了这种限制。因此,在C++11中,可将转换运算符声明为显式的。

应谨慎地使用隐式转换函数。通常,最好选择仅在被显式调用时才会执行的函数。

11.9.3.4 转换函数选择问题

有两种方式:

11.9.3.4.1 第一种
成员函数
Stonewt Stonewt::operator+(const Stonewt & st) const
{
    double pds = pounds + st.pounds;
    Stonewt sum(pds);
    return sum;
}
And 友元函数
Stonewt operator+(const Stonewt & st1, const Stonewt & st2)
{
    double pds = st1.pounds + st2.pounds;
    Stonewt sum(pds);
    return sum;
}

针对这两个操作符重载,第一个和第二个+重载都允许使用以下语句:

语句组合1:

Stonewt jennySt(9, 12);
Stonewt bennySt(12, 8);
Stonewt total;
total = jennySt + bennySt;

语句组合2:需要隐式调用构造函数转换类型

Stonewt jennySt(9, 12);
double kennyD = 176.0;
Stonewt total;
total = jennySt + kennyD;

但是,只有第二个+重载允许使用以下语句,但是需要隐式调用构造函数转换类型:

Stonewt jennySt(9, 12);
double pennyD = 146.0;
Stonewt total;
total = pennyD + jennySt;

原因是:转换发生在成员函数参数,而不是成员函数调用者。

11.9.3.4.2 第二种方式
Stonewt operator+(double x); // member function
friend Stonewt operator+(double x, Stonewt & s);

语句组合1和语句组合2都可以匹配第一个,但是语句组合1需要使用转换函数double(),语句组合2匹配第二个。

11.9.3.4.3 两种方式对比

第一种方式的程序更短,因为定义的函数更少。但是需要调用构造函数形成临时的对象,增加了程序运行时间和内存开销。第二种方式程序更长,程序员所做的工作更多,但是运行速度较快。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jasmine-Lily

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值