C++高级编程(第4版)个人笔记 15.8 - 转换运算符 | 转换运算函数

参考书籍:
《C++高级编程》(第4版)》
在这里插入图片描述


下面是一个简单的"分数"类:

//Fraction.h
//分数类

#ifndef FRACTION_FRACTION_H
#define FRACTION_FRACTION_H

class Fraction {
public:
    Fraction(int num, int den = 1)
            : m_numerator{num}, m_denominator{den} {};
    
private:
    int m_numerator;    //分子
    int m_denominator;  //分母
};

#endif //FRACTION_FRACTION_H

考虑下面两行代码:

Fraction x{7, 2};   //3.5
double y =  x ;  //Error: Cannot convert 'Fraction' to 'double' in initialization

Fraction可以转换为double类型,因此看上去把Fraction赋值给double变量是符合逻辑的。但不能这么做。编译器会不知道如何将Fraction转换为double类型。你可能会通过下述方式迫使编译器进行这种转换:

double y =  (double)x ;  //Error: invalid cast from type 'Fraction' to type 'double'

首先,上述代码依然无法编译,因为编译器还是不知道如何将Fraction转换为double类型。从这行代码中编译器已知你想让编译器做这种转换,所以如果编译器知道如何转换,就会进行转换。其次一般情况下,最好不要在程序中添加这种无理由的类型转换。
如果想允许这类赋值,就必须告诉编译器具体如何去执行。确切地讲,可编写一个将Fraction转换为double类型的转换运算符(转换函数)。原型如下所示:

operator double() const

函数名为operator double。它没有返回类型,因为返回类型是通过运算符名称确定的:double。这个函数是const的,因为这个函数不会修改被调用的对象,转换不是改变。实现如下所示:

Fraction::operator double() const {
    return (double) m_numerator/m_denominator;
}

这就完成了从Fractiondouble类型的转换运算符的编写。现在编译器接受下这行代码,并在运行时执行正确的操作。

Fraction x{7, 2};   //3.5
double y =  (double)x ;
std::cout << y  << std::endl;   //3.5
std::cout << x+7 << std::endl;  //调用operator double()将x转为3.5

运行结果:
在这里插入图片描述

关于第4行代码,编译器编译时会首先判断是否有全局函数+,没有编译器再次判断代码是否有设计转换运算符。

可用同样的语法编写任何类型的转换运算符。例如,下面是从Fractionstd::string的转换运算符(分子分母小于10):

Fraction::operator std::string() const {
        std::string s{""};
        s.push_back('0'+m_numerator);
        s.push_back('/');
        s.push_back('0'+m_denominator);
        return s;
}

现在可以编写以下代码:

std::string s = x;
std::cout << s << std::endl;    // 7/2

运行结果:
在这里插入图片描述

non-explicit-one-argument ctor

现有代码:

//Fraction.h
//分数类

#ifndef FRACTION_FRACTION_H
#define FRACTION_FRACTION_H

#include <string>
#include <iostream>

class Fraction {
public:
    Fraction(int num, int den = 1)
            : m_numerator{num}, m_denominator{den} {};

    Fraction operator+(const Fraction& F){
        return Fraction(m_numerator*F.m_denominator + m_denominator*F.m_numerator,
                        m_denominator*F.m_denominator);
    }

    void printFraction(){
        std::cout << m_numerator << " / " << m_denominator << std::endl;
    }

private:
    int m_numerator;    //分子
    int m_denominator;  //分母
};

#endif //FRACTION_FRACTION_H

one-argument的意思就是,只要一个实参就够了,你也可以给两个实参。Fraction的构造函数(11行)中第二个参数已经有默认值1(对于整数,转为分数分母本就是1),所以可以只要一个实参。又没有加explicit,所以Fraction就是non-explicit-one-arugument cotr

Fraction x{7, 2};   //3.5
Fraction y = x + 4;   // 调用 non-explicit ctor 将4转为Fraction(4,1),然后调用operator+
y.printFraction();

运行结果:
在这里插入图片描述

使用显示转换运算符解决多义性问题(现有最新编译器已解决)

有如下代码:

//Fraction.h
//分数类

#ifndef FRACTION_FRACTION_H
#define FRACTION_FRACTION_H

#include <string>
#include <iostream>

class Fraction {
public:
    Fraction(int num, int den = 1)
            : m_numerator{num}, m_denominator{den} {};

    operator double() const{
        return (double) m_numerator/m_denominator;
    }

    Fraction operator+(const Fraction& F){
        return Fraction(m_numerator*F.m_denominator + m_denominator*F.m_numerator,
                        m_denominator*F.m_denominator);
    }

private:
    int m_numerator;    //分子
    int m_denominator;  //分母
};
#endif //FRACTION_FRACTION_H
Fraction x{7, 2};   //3.5
Fraction y = x + 4;   //Error: Ambiguous overload for 'operator+' (operand types are 'Fraction' and 'double')
y.printFraction();

注意,为Fraction对象编写double转换运算符时会引入多义性问题。 例如下面第2行代码:

Fraction x{7,2};   // 7/2
double y = x + 4.0;

现在这行代码无法成功编译。在编写operator double() 前,这行代码可编译,那么现在出了什么问题?问题在于,编译器不知道应该通过operator double() 将x 对象转换为double类型,再执行double加法,还是通过double构造函数将4转换为Fraction,再执行Fraction加法。在编写 operator double() 前,编译器只有一个选择:通过double构造函数将4转换为Fraction,再执行Fraction加法。然而,现在编译器可执行两种操作。编译器不想做出让人不喜欢的决定,因此拒绝做出任何决定。
在C++11之前,通常解决这个难题的方法是将构造函数标记为explicit,以避免使用这个构造函数进行自动转换(见第9章)。然而,我们不想把这个构造函数标记为explicit,因为通常希望进行从doubleFraction的自动类型转换。自C++11以后,可将double类型转换运算符标记为explicit以解决这个问题:

explicit operator double() const

下面代码演示了这种方法的应用:

Fraction x{7,2};   // 7/2
double y = (double)x;   //3.5
double z = (double)(x + 5);     //8.5
以上代码并不完善,仅做理解使用

欢迎关注公众号:c_302888524
发送:“C++高级编程(第3版)” 获取电子书
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值