暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

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

东拉西扯 2020-04-06
157

参考书籍: 《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类型的转换运算符的编写。现在编译器接受下这行代码,并在运行时执行正确的操作。

            运行结果:

            关于第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.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对象编写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以解决这个问题:

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

                        Fraction x{7,2};   // 7/2
                        double y = (double)x; //3.5
                        double z = (double)(x + 5);     //8.5
                        复制

                        以上代码并不完善,仅做理解使用

                        欢迎关注公众号:c_302888524 发送:"C++高级编程(第3版)" 获取电子书


                        文章转载自东拉西扯,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                        评论