重载操作符与转换(下)

  • 转换与类类型

可用一个实参调用的非 explicit 构造函数定义一个隐式转换。当提供了实参类型的对象而需要一个类类型的对象时,编译器将使用该转换。这种构造函数定义了到类类型的转换。除了定义到类类型的转换之外,我们还可以定义从类类型的转换。即,我们可以定义转换操作符,给定类类型的对象,该操作符将产生其他类型的对象。像其他转换一样,编译器将自动应用这个转换。在介绍如何定义这种转换之前,将说明它们为什么可能有用。

假定想要定义一个名为 SmallInt 的类,该类实现安全小整数,这个类将使我们能够定义对象以保存与 8 位 unsigned char 同样范围的值,即,0 到 255。这个类可以捕获下溢和上溢错误,因此使用起来比内置 unsigned char 更安全。我们希望这个类定义 unsigned char 支持的所有操作。具体而言,我们想定义 5 个算术操作符(+-*/%)及其对应的复合赋值操作符,4 个关系操作符(<<=>>=),以及相等操作符(==!=)。显然,需要定义 16 个操作符。而且,我们希望可以在混合模式表达式中使用这些操作符。例如,应该可以将两个 SmallInt 对象相加,也可以将任意算术类型加到 SmallInt。通过为每个操作符定义三个实例来达到目标:

int operator+(int, const SmallInt&);
     int operator+(const SmallInt&, int);
     SmallInt operator+(const SmallInt&, const SmallInt&);

因为存在从任意算术类型到 int 的转换,这三个函数可以涵盖支持 SmallInt 对象的混合模式使用的要求。但是,这个设计仅仅接近内置整数运算的行为,它不能适当处理浮点类型混合模式操作,也不能适当支持 longunsigned intunsigned long 的加运算。问题在于这个设计将所有算术类型(甚至包括那些比 int 大的)转换为 int 并进行 int 加运算。即使忽略浮点或大整型操作数的问题,如果要实现这个设计,也必须定义 48 个操作符!幸好,C++ 提供了一种机制,利用这种机制,一个类可以定义自己的转换,应用于其类类型对象。对 SmallInt 而言,可以定义一个从 SmallIntint 类型的转换。如果定义了该转换,则无须再定义任何算术、关系或相等操作符。给定到 int 的转换,SmallInt 对象可以用在任何可用 int 值的地方。//弊端:必须对加减乘除等所有操作符进行重载;必须对与整型、浮点型、double型等所有类型的操作都进行重载。

如果存在一个到 int 的转换,则以下代码:

SmallInt si(3);
     si + 3.14159;         // convert si to int, then convert to double

可这样确定:

si 转换为 int 值。将所得 int 结果转换为 double 值并与双精度字面值常量 3.14159 相加,得到 double 值。

  • 转换操作符

转换操作符是一种特殊的类成员函数。它定义将类类型值转变为其他类型值的转换。转换操作符在类定义体内声明,在保留字 operator 之后跟着转换的目标类型:

class SmallInt {
     public:
         SmallInt(int i = 0): val(i)
         { if (i < 0 || i > 255)
            throw std::out_of_range("Bad SmallInt initializer");
         }
         operator int() const { return val; }
     private:
         std::size_t val;
     };

转换函数采用如下通用形式:

operator type();

这里,type 表示内置类型名、类类型名或由类型别名定义的名字。对任何可作为函数返回类型的类型(除了 void 之外)都可以定义转换函数。一般而言,不允许转换为数组或函数类型,转换为指针类型(数据和函数指针)以及引用类型是可以的。转换函数必须是成员函数,不能指定返回类型,并且形参表必须为空

虽然转换函数不能指定返回类型,但是每个转换函数必须显式返回一个指定类型的值。例如,operator int 返回一个 int 值;如果定义 operator Sales_item,它将返回一个 Sales_item 对象,诸如此类。转换函数一般不应该改变被转换的对象。因此,转换操作符通常应定义为 const 成员

  • 使用类类型转换

只要存在转换,编译器将在可以使用内置转换的地方自动调用它:

在表达式中:

SmallInt si;
     double dval;
     si >= dval          // si converted to int and then convert to double

在条件中:

if (si)                // si converted to int and then convert to bool

将实参传给函数或从函数返回值:

int calc(int);
     SmallInt si;
     int i = calc(si);      // convert si to int and call calc

作为重载操作符的操作数:

// convert si to int then call opeator<< on the int value
     cout << si << endl;

在显式类型转换中:

int ival;
     SmallInt si = 3.541; //
     instruct compiler to cast si to int
     ival = static_cast<int>(si) + 3;

使用转换函数时,被转换的类型不必与所需要的类型完全匹配。必要时可在类类型转换之后跟上标准转换以获得想要的类型。例如,在一个 SmallInt 对象与一个 double 值的比较中:

SmallInt si;
     double dval;
     si >= dval // si converted to int and then convert to double
  • 只能应用一个类类型转换

类类型转换之后不能再跟另一个类类型转换。如果需要多个类类型转换,则代码将出错。例如,假定有另一个类 Integral,它可以转换为 SmallInt 但不能转换为 int

// class to hold unsigned integral values
     class Integral {
     public:
         Integral(int i = 0): val(i) { }
         operator SmallInt() const { return val % 256; }//不能再转换为int了
     private:
         std::size_t val;
     };

可以在需要 SmallInt 的地方使用 Integral,但不能在需要 int 的地方使用 Integeral

int calc(int);
     Integral intVal;
     SmallInt si(intVal);  // ok: convert intVal to SmallInt and copy to si
     int i = calc(si);     // ok: convert si to int and call calc
     int j = calc(intVal); // error: no conversion to int from Integral
时间: 2024-07-30 10:17:59

重载操作符与转换(下)的相关文章

C++ Primer 学习笔记_64_重载操作符与转换 --转换与类类型【下】

重载操作符与转换 --转换与类类型[下] 四.重载确定和类的实参 在需要转换函数的实参时,编译器自动应用类的转换操作符或构造函数.因此,应该在函数确定期间考虑类转换操作符.函数重载确定由三步组成: 1)确定候选函数集合:这些是与被调用函数同名的函数. 2)选择可行的函数:这些是形参数目和类型与函数调用中的实参相匹配的候选函数.选择可行函数时,如果有转换操作,编译器还要确定需要哪个转换操作来匹配每个形参. 3)选择最佳匹配的函数.为了确定最佳匹配,对将实参转换为对应形参所需的类型转换进行分类.对于

C++ Primer 学习笔记_60_重载操作符与转换 --赋值、下标、成员訪问操作符

重载操作符与转换 --赋值.下标.成员訪问操作符 一.赋值操作符 类赋值操作符接受类类型形參,通常该形參是对类类型的const引用,但也能够是类类型或对类类型的非const引用.假设未定义这个操作符,则编译器将合成它.类赋值操作符必须是类的成员,以便编译器能够知道是否须要合成一个.并且还能够为一个类定义很多附加的赋值操作符,这些赋值操作符会由于右操作数的不同而构成重载!如string类型: string car("Volks"); car = "Studebaker"

C++ 重载操作符与转换

<C++ Primer 4th>读书笔记 重载操作符是具有特殊名称的函数:保留字 operator 后接需定义的操作符号. Sales_item operator+(const Sales_item&, const Sales_item&); 声明了加号操作符,可用于将两个 Sales_item 对象“相加”并获得一个 Sales_item 对象的副本. 不能重载的操作符 :: .* . ?: 通过连接其他合法符号可以创建新的操作符.例如,定义一个 operator**以提供求

【C++ Primer】重载操作符与转换

十四.重载操作符与转换 1. 重载操作符的定义 重载操作符必须具有至少一个类类型或枚举类型的操作数,这条规则强制重载操作符不能重新定义用于内置类型对象的操作符含义. int operator +(int, int) // 错误,内置数据类型不能重载操作符 重载操作符,操作符的优先级.结合性或操作数数目不能改变.重载操作符并不保证操作数的求值顺序,不再具备短路求值特性,因此,重载&&.||.逗号操作符不是一种好的做法.除了函数调用操作符 operator()外,重载操作符时使用默认实参是非法

C++ Primer 学习笔记_58_重载操作符与转换 --重载操作符的定义

重载操作符与转换 --重载操作符的定义 引言: 明智地使用操作符重载可以使类类型的使用像内置类型一样直观! 重载操作符的定义 重载操作符是具有特殊名称的函数:保留字operator后接定义的操作符符号.如: Sales_item operator+(const Sales_item &,const Sales_item &); 除了函数调用操作符之外,重载操作符的形参数目(包括成员函数的隐式this指针)与操作符的操作数数目相同.函数调用操作符可以接受任意数目的操作数. 1.重载的操作符名

C++ Primer 学习笔记_59_重载操作符与转换 --输入/输出、算术/关系操作符

重载操作符与转换 --输入/输出.算术/关系操作符 支持I/O操作的类所提供的I/O操作接口,一般应该与标准库iostream为内置类型定义的接口相同,因此,许多类都需要重载输入和输出操作符. 一.输出操作符<<的重载 为了与IO标准库一致,操作符应接受ostream&作为第一个形参,对类类型const对象的引用作为第二个形参,并返回ostream形参的引用! ostream &operator<<(ostream &os,const ClassType &

重载操作符与转换

?  重载操作符的定义 重载操作符是具有特殊名称的函数:保留字operator 后接需定义的操作符号.像任意其他函数一样,重载操作符具有返回类型和形参表,如下语句: Sales_item  operator+(const Sales_item&, constSales_item&); ?  可以和不可以重载的操作符 ?  重载操作符必须具有一个类类型操作数,这条规则强制重载操作符不能重新定义用于内置类型对象的操作符的含义. ?  操作符的优先级.结合性或操作数不能改变. ?  重载操作符并

C++ Primer 学习笔记_63_重载操作符与转换 --转换与类类型【上】

重载操作符与转换 --转换与类类型[上] 引言: 在前面我们提到过:可以用一个实参调用的非explicit构造函数定义一个隐式转换.当提供了实参类型的对象需要一个类类型的对象时,编译器将使用该转换.于是:这种构造函数定义了到类类型的转换. 除了定义到类类型的转换之外,还可以定义从类类型到其他类型的转换.即:我们可以定义转换操作符,给定类类型的对象,该操作符将产生其他类型的对象.和其他转换一样,编译器将自动应用这个转换. 一.转换为什么有用? 定义一个SmallInt的类,该类实现安全小整数,这个

C++ Primer 学习笔记_61_重载操作符与转换 --自增/自减操作符

重载操作符与转换 --自增/自减操作符 引言: 自增,自减操作符常常由诸如迭代器这种类实现,这种类提供相似于指针的行为来訪问序列中的元素.比如,能够定义一个类,该类指向一个数组并为该数组中的元素提供訪问检查: class CheckedPtr { public: //这个类没有默认构造函数,必须提供指向数组的指针. /**构造函数的參数是两个指针:一个指向数组的開始,还有一个指向数组的末端. *构造函数用这两个指针初始化 beg 和 end *并将 curr 初始化为指向第一个元素 */ Che

C++ Primer 学习笔记_62_重载操作符与转换 --调用操作符和函数对象

重载操作符与转换 --调用操作符和函数对象 引言: 能够为类类型的对象重载函数调用操作符:一般为表示操作的类重载调用操作符! struct absInt { int operator() (int val) { return val > 0 ? val : -val; } }; 通过为类类型的对象提供一个实參表而使用调用操作符,所用的方式看起来系那个一个函数调用: absInt absObj; int i = -1; cout << absObj(i) << endl; 虽然