c++类——运算符重载(三)

本文最后更新于:1 年前

类的类型转换:

(一)概述

数据类型转换在程序编译或者运行时实现,当使用基本类型的时候,如int和double之间可以直接进行灵活的类型转换,如果我们需要把一个类对象转换为其他类型类的对象或者基本类型呢?

类对象的类型转换可由两种方式实现:构造函数和转换函数(即类型转换运算符重载函数)。

前者是把其他类型转换为该类对象类型,后者是把该类对象类型转换为其他类型。

本文只考虑后者(前者已经在其他文章说明过)。

(二)重载类型转换运算符

  • 类型转换运算符函数是一种特殊的成员函数(不可以是友元函数),提供类对象之间或者类对象到基本类型显示类型转换的机制。

  • 语法形式为: 类名::operator type() { 函数体 }

    type一般为用户定义的其他类类型或者内置的数据类型(可以是typedef定义的别名),不允许是void,一般也不允许是数组或者函数类型,但可以是指针或者引用类型。

    该函数无参数和返回类型,但是需要return语句,返回type类型的对象/数据。

  • 功能:将该类对象转换为type类型的对象/数据。

  • 一般情况下会用const修饰,因为其通常不会改变待转换对象的状态。、

(三)重载类型转换运算符的二义性和explicit

  • 类型转换运算符可能产生意外结果:

    对于类向bool的类型转换,有一个需要注意的问题。

    我们以istream类对象cin为例:

    1
    2
    int a=10;
    cin<<a;

    虽然istream未定义<<,代码应当产生错误,但是实际上此处istream的bool类型转换运算符将cin转换成了bool,而这个bool值再经过第二次类型转换提升为int作为了内置的左移运算符的左侧运算对象,这样会使得这个bool值(1或0)被左移了10个位置,虽然没有报错,但是结果与我们的预期完全不同。

    解决方法:

    IO类型中定义了向void*的转换规则来避免该问题。

    新c++11标准中,IO标准库定义了一个向bool的显示类型转换来避免问题。

    综上,在我们定义时,通常需要对operator bool用explicit关键字声明为显式的类型转换,避免隐式的。

  • explicit关键字:在类型转换运算符上声明,每次的类型转换需要显式的强制类型转换,可以避免隐式类型转换。

    但是,如果表达式被用作条件,编译器会将显式的类型转换自动应用于它,即在以下场景时发生显式的类型转换被隐式地执行:

    1. if、while及do语句的条件部分。

    2. for语句头的条件表达式。

    3. 作为逻辑非(!)/或(||)/与(&&)的运算对象。

    4. 条件运算符(?:)的条件表达式。

  • 类型转换运算符的二义性:

    通常情况下,不要为类定义相同的类型转换,也不要在类中定义两个及两个以上转换源或转换目标是算术类型的转换来避免二义性转换。

    以下是二义性情况:

    1. 两个类提供了相同的类型转换。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      //相同的类型转换:
      //main外:
      class B;
      class A
      {
      public:
      A() = default;
      A(const B&){}
      };
      class B
      {
      public:
      operator A()const { A temp; return temp; }
      };
      A f(const A& a)
      {
      return a;
      }
      //main内:
      A a;
      B b;
      a = f(b); //b需要转化为a,此时发生错误:是调用A中的转换构造函数把对象b变成a,还是调用B中的重载类型转换运算符将b变成a?

      解决方法:显式调用。

      1
      2
      3
      //修改a = f(b);
      a = f(A(b);
      a = f(b.operator A());
    2. 类定义了多个转换规则,某些规则可以通过其他类型转换实现(多见于算术运算符):

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      //main外:
      class A
      {
      private:
      double a;
      public:
      A(int i = 0) :a(i) {}
      A(double d) :a(d) {}
      operator int() const { return int(a); }
      operator double() const { return a; }
      };
      void f(long double){}
      //main()内:
      int main()
      {
      A a;
      f(a); //二义性,是从double还是int的运算符重载提升为long double?
      long d = a;//二义性,是从double还是int的运算符重载提升为long double?
      return 0;
      }

      如果有谁优于的原则不会产生此问题。(本题的两个提升地位相同)

    3. 重载运算符和类型转换函数结合导致了二义性:

      一般是因为在某些情况下既可以是类对象转换为基本数据类型,或者基本数据类型转变为类类型。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      //main外:
      class A
      {
      friend A operator+(const A& a1,const A& a2);
      private:
      int a;
      public:
      A(int i = 0) :a(i) {}
      operator int()const { return a; }
      };
      A operator+(const A& a1, const A& a2)
      {
      return A(a1.a + a2.a);
      }
      //main()内:
      int main()
      {
      A a1, a2;
      A a3 = a1 + a2; //此处无问题,可以使用重载的+运算符
      a3 = 1 + a1; //二义性错误,不知道是将1转为类对象还是将a1转为int类型。
      a3 = a2 + 1; //同上
      return 0;
      }

      解决方法:在构造函数上加explicit或者直接进行显式的强制类型转化。

      1
      2
      3
      4
      5
      //可以是;
      explicit operator int()const { return a; }
      //或者是:
      a3 = A(1) + a1
      a3 = int(a2) + 1

      注意:出现此种情况说明我们的代码设计不够好。

(四)代码展示

对上一章中的newvector2提供相关的类型转化(转化为vector2和转换成基本类型double)

1
2
3
4
//vector2.h
//添加两个成员函数
operator double();
operator vector2();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//vector2。cpp
//定义两个成员函数
double newvector2::operator()(double xx, double yy)
{
return sqrt(xx * xx + yy * yy);
}

newvector2::operator double()
{
return x;
}
newvector2::operator vector2()
{
return vector2(x, y);
}
1
2
3
4
5
6
7
8
9
10
11
//main.cpp
#include"vector2.h"
int main()
{
newvector2 newv(11,13,"aaaa");
vector2 v = newv;
double x = newv;
cout << "v:" << v;
cout << "x:" << x << endl;
return 0;
}

c++类——运算符重载(三)
https://github.com/xiaohei07/xiaohei07.github.io/2023/03/24/c++类——运算符重载(三)/
作者
07xiaohei
发布于
2023年3月24日
许可协议