14.1. Defining an Overloaded Operator14.1. 重载操作符的定义Overloaded operators are functions with special names: the keyword operator followed by the symbol for the operator being defined. Like any other function, an overloaded operator has a return type and a parameter list. 重载操作符是具有特殊名称的函数:保留字 operator 后接需定义的操作符号。像任意其他函数一样,重载操作符具有返回类型和形参表,如下语句: Sales_item operator+(const Sales_item&, const Sales_item&); declares the addition operator that can be used to "add" two Sales_item objects and yields a copy of a Sales_item object. 声明了加号操作符,可用于将两个 Sales_item 对象“相加”并获得一个 Sales_item 对象的副本。 With the exception of the function-call operator, an overloaded operator has the same number of parameters (including the implicit this pointer for member functions) as the operator has operands. The function-call operator takes any number of operands. 除了函数调用操作符之外,重载操作符的形参数目(包括成员函数的隐式 this 指针)与操作符的操作数数目相同。函数调用操作符可以接受任意数目的操作数。 Overloaded Operator Names重载的操作名Table 14.1 on the next page lists the operators that may be overloaded. Those that may not be overloaded are listed in Table 14.2. 表 14.1 列出了可以重载的操作符,不能重载的在表 14.2 列出。 Table 14.1. Overloadable Operators表 14.1. 可重载的操作符
Table 14.2. Operators That Cannot Be Overloaded表 14.2. 不能重载的操作符
New operators may not be created by concatenating other legal symbols. For example, it would be illegal to attempt to define an operator** to provide exponentiation. Overloading new and delete is described in Chapter 18 (p. 753). 通过连接其他合法符号可以创建新的操作符。例如,定义一个 operator** 以提供求幂运算是合法的。第十八章将介绍重载 new 和 delete。 Overloaded Operators Must Have an Operand of Class Type重载操作符必须具有一个类类型操作数The meaning of an operator for the built-in types may not be changed. For example, the built-in integer addition operation cannot be redefined: 用于内置类型的操作符,其含义不能改变。例如,内置的整型加号操作符不能重定义:
// error: cannot redefine built-in operator for ints
int operator+(int, int);
Nor may additional operators be defined for the built-in data types. For example, an operator+ taking two operands of array types cannot be defined. 也不能为内置数据类型重定义加号操作符。例如,不能定义接受两个数组类型操作数的 operator+。
Precedence and Associativity Are Fixed优先级和结合性是固定的The precedence (Section 5.10.1, p. 168), associativity, or number of operands of an operator cannot be changed. Regardless of the type of the operands and regardless of the definition of what the operations do, this expression 操作符的优先级(第 5.10.1 节)、结合性或操作数目不能改变。不管操作数的类型和操作符的功能定义如何,表达式 x == y +z; always binds the arguments y and z to operator+ and uses that result as the right-hand operand to operator==. 总是将实参 y 和 z 绑定到 operator+,并且将结果用作 operator== 右操作数。 Four symbols (+, -, *, and &) serve as both unary and binary operators. Either or both of these operators can be overloaded. Which operator is being defined is controlled by the number of operands. Default arguments for overloaded operators are illegal, except for operator(), the function-call operator. 有四个符号(+, -, * 和 &)既可作一元操作符又可作二元操作符,这些操作符有的在其中一种情况下可以重载,有的两种都可以,定义的是哪个操作符由操作数数目控制。除了函数调用操作符 operator() 之外,重载操作符时使用默认实参是非法的。 Short-Ciruit Evaluation Is Not Preserved不再具备短路求值特性Overloaded operators make no guarantees about the order in which operands are evaluated. In particular, the operand-evaluation guarantees of the built-in logical AND, logical OR (Section 5.2, p. 152), and comma (Section 5.9, p. 168) operators are not preserved. Both operands to an overloaded version of && or || are always evaluated. The order in which those operands are evaluated is not stipulated. The order in which the operands to the comma are evaluated is also not defined. For this reason, it is usually a bad idea to overload &&, ||, or the comma operator. 重载操作符并不保证操作数的求值顺序,尤其是,不会保证内置逻辑 AND、逻辑 OR(第 5.2 节)和逗号操作符(第 5.9 节)的操作数求值。在 && 和 || 的重载版本中,两个操作数都要进行求值,而且对操作数的求值顺序不做规定。因此,重载 &&、|| 或逗号操作符不是一种好的做法。 Class Member versus Nonmember类成员与非成员Most overloaded operators may be defined as ordinary nonmember functions or as class member functions. 大多数重载操作符可以定义为普通非成员函数或类的成员函数。
An overloaded unary operator has no (explicit) parameter if it is a member function and one parameter if it is a nonmember function. Similarly, an overloaded binary operator would have one parameter when defined as a member and two parameters when defined as a nonmember function. 重载一元操作符如果作为成员函数就没有(显式)形参,如果作为非成员函数就有一个形参。类似地,重载二元操作符定义为成员时有一个形参,定义为非成员函数时有两个形参。 The Sales_item class offers a good example of member and nonmember binary operators. We know that the class has an addition operator. Because it has an addition operator, we ought to define a compound-assignment (+=) operator as well. This operator will add the value of one Sales_item object into another. 类 Sales_item 中给出了成员和非成员二元操作符的良好例子。我们知道该类有一个加号操作符。因为它有一个加号操作符,所以也应该定义一个复合赋值(+=)操作符,该操作符将一个 Sales_item 对象的值加至另一个 Sales_item 对象。 Ordinarily we define the arithmetic and relational operators as nonmember functions and we define assignment operators as members: 一般将算术和关系操作符定义非成员函数,而将赋值操作符定义为成员: // member binary operator: left-hand operand bound to implicit this pointer Sales_item& Sales_item::operator+=(const Sales_item&); // nonmember binary operator: must declare a parameter for each operand Sales_item operator+(const Sales_item&, const Sales_item&); Both addition and compound assignment are binary operators, yet these functions define a different number of parameters. The reason for the discrepancy is the this pointer. 加和复合赋值都是二元操作符,但这些函数定义了不同数目的形参,差异的原因在于 this 指针。 When an operator is a member function, this points to the left-hand operand. Thus, the nonmember operator+ defines two parameters, both references to const Sales_item objects. Even though compound assignment is a binary operator, the member compound-assignment operator takes only one (explicit) parameter. When the operator is used, a pointer to the left-hand operand is automatically bound to this and the right-hand operand is bound to the function's sole parameter. 当操作符为成员函数,this 指向左操作数,因此,非成员 operator+ 定义两个形参,都引用 const Sales_item 对象。即使复合赋值是二元操作符,成员复合赋值操作符也只接受一个(显式的)形参。使用操作符时,一个指向左操作数的指针自动绑定到 this,而右操作符限定为函数的唯一形参。 It is also worth noting that compound assignment returns a reference and the addition operator returns a Sales_item object. This difference matches the return types of these operators when applied to arithmetic types: Addition yields an rvalue and compound assignment returns a reference to the left-hand operand. 复合赋值返回一个引用而加操作符返回一个 Sales_item 对象,这也没什么。当应用于算术类型时,这一区别与操作符的返回类型相匹配:加返回一个右值,而复合赋值返回对左操作数的引用。 Operator Overloading and Friendship操作符重载和友元关系When operators are defined as nonmember functions, they often must be made friends (Section 12.5, p. 465) of the class(es) on which they operate. We'll see later in this chapter two reasons why operators might be defined as nonmembers. In such cases, the operator often needs access to the private parts of the class. 操作符定义为非成员函数时,通常必须将它们设置为所操作类的友元(12.5 节)。在本章的后面部分,将给出操作符可以定义为非成员的两个原因。在这种情况下,操作符通常需要访问类的私有部分。 Our Sales_item class is again a good example of why some operators need to be friends. It defines one member operator and has three nonmember operators. Those nonmember operators, which need access to the private data members, are declared as friends: Sales_item 类也是说明为何有些操作符需要设置为友元的一个好例子。它定义了一个成员操作符,并且有三个非成员操作符。这些非成员操作符需要访问私有数据成员,声明为友元: class Sales_item { friend std::istream& operator>> (std::istream&, Sales_item&); friend std::ostream& operator<< (std::ostream&, const Sales_item&); public: Sales_item& operator+=(const Sales_item&); }; Sales_item operator+(const Sales_item&, const Sales_item&); That the input and output operators need access to the private data should not be surprising. After all, they read and write those members. On the other hand, there is no need to make the addition operator a friend. It can be implemented using the public member operator+=. 输入和输出操作符需要访问 private 数据不会令人惊讶,毕竟,它们的作用是读入和写出那些成员。另一方面,不需要将加操作符设置为友元,它可以用 public 成员 operator+= 实现。 Using Overloaded Operators使用重载操作符We can use an overloaded operator in the same way that we'd use the operator on operands of built-in type. Assuming item1 and item2 are Sales_item objects, we might print their sum in the same way that we'd print the sum of two ints: 使用重载操作符的方式,与内置类型操作数上使用操作符的方式一样。假定 item1 和 item2 是 Sales_item 对象,可以打印它们的和,就像打印两个 int 的和一样: cout << item1 + item2 << endl; This expression implicitly calls the operator+ that we defined for Sales_items. 这个表达式隐式调用为 Sales_items 类而定义的 operator+。 We also can call an overloaded operator function in the same way that we call an ordinary function: We name the function and pass an appropriate number of arguments of the appropriate type: 也可以像调用普通函数一样调用重载操作符函数,指定函数并传递适当类型适当数目的形参:
// equivalent direct call to nonmember operator function
cout << operator+(item1, item2) << endl;
This call has the same effect as the expression that added item1 and item2. 这个调用与 item1 和 item2 相加的表达式等效。 We call a member operator function the same way we call any other member function: We name an object on which to run the function and then use the dot or arrow operator to fetch the function we wish to call passing the required number and type of arguments. In the case of a binary member operator function, we must pass a single operand: 调用成员操作符函数与调用任意其他函数是一样的:指定运行函数的对象,然后使用点或箭头操作符获取希望调用的函数,同时传递所需数目和类型的实参。对于二元成员操作符函数的情况,我们必须传递一个操作数: item1 += item2; // expression based "call" item1.operator+=(item2); // equivalent call to member operator function Each of these statements adds the value of item2 into item1. In the first case, we implicitly call the overloaded operator function using expression syntax. In the second, we call the member operator function on the object item1. 两个语句都将 item2 的值加至 item1。第一种情况下,使用表达式语法隐式调用重载操作符函数:第二种情况下,在 item1 对象上调用成员操作符函数。 14.1.1. Overloaded Operator Design14.1.1. 重载操作符的设计When designing a class there are some useful rules of thumb to keep in mind when deciding which, if any, overloaded operators to provide. 设计类的时候,需要记住一些有用的经验原则,可以有助于确定应该提供哪些重载操作符(如果需要提供)。 Don't Overload Operators with Built-in Meanings不要重载具有内置含义的操作符The assignment, address of, and comma operators have default meanings for operands of class types. If there is no overloaded version specified, the compiler defines its own version of these operators: 赋值操作符、取地址操作符和逗号操作符对类类型操作数有默认含义。如果没有特定重载版本,编译器就自己定义以下这些操作符。
The meaning of these operators can be changed by redefining them for operands of a given class type. 通过为给定类类型的操作数重定义操作符,可以改变这些操作符的含义。
We sometimes must define our own version of assignment. When we do so, it should behave analogously to the synthesized operators: After an assignment, the values in the left-hand and right-hand operands should be the same and the operator should return a reference to its left-hand operand. Overloaded assignment should customize the built-in meaning of assignment, not circumvent it. 有时我们需要定义自己的赋值运算。这样做时,它应表现得类似于合成操作符:赋值之后,左右操作数的值应是相同的,并且操作符应返回对左操作数的引用。重载的赋值运算应在赋值的内置含义基础上进行定制,而不是完全绕开。 Most Operators Have No Meaning for Class Objects大多数操作符对类对象没有意义Operators other than assignment, address-of, and comma have no meaning when applied to an operand of class type unless an overloaded definition is provided. When designing a class, we decide which, if any, operators to support. 除非提供了重载定义,赋值、取地址和逗号操作符对于类类型操作数没有意义。设计类的时候,应该确定要支持哪些操作符。 The best way to design operators for a class is first to design the class' public interface. Once the interface is defined, it is possible to think about which operations should be defined as overloaded operators. Those operations with a logical mapping to an operator are good candidates. For example, 为类设计操作符,最好的方式是首先设计类的公用接口。定义了接口之后,就可以考虑应将哪些操作符定义为重载操作符。那些逻辑上可以映射到某个操作符的操作可以考虑作为候选的重载操作符。例如:
Compound Assignment Operators复合赋值操作符If a class has an arithmetic (Section 5.1, p. 149) or bitwise (Section 5.3, p. 154) operator, then it is usually a good idea to provide the corresponding compound-assignment operator as well. For example, our Sales_item class defined the + operator. Logically, it also should define +=. Needless to say, the += operator should be defined to behave the same way the built-in operators do: Compound assignment should behave as + followed by =. 如果一个类有算术操作符(第 5.1 节)或位操作符(第 5.3 节),那么,提供相应的复合赋值操作符一般是个好的做法。例如,Sales_item 类定义了 + 操作符,逻辑上,它也应该定义 +=。不用说,操作符的行为应定义为与内置操作符一样:复合赋值的行为应与 + 之后接着 = 类似。 Equality and Relational Operators相等和关系操作符Classes that will be used as the key type of an associative container should define the < operator. The associative containers by default use the < operator of the key type. Even if the type will be stored only in a sequential container, the class ordinarily should define the equality (==) and less-than (<) operators. The reason is that many algorithms assume that these operators exist. As an example, the sort algorithm uses < and find uses ==. 将要用作关联容器键类型的类应定义 < 操作符。关联容器默认使用键类型的 < 操作符。即使该类型将只存储在顺序容器中,类通常也应该定义相等(==)和小于(<)操作符,理由是许多算法假定这个操作符存在。例如 sort 算法使用 < 操作符,而 find 算法使用 == 操作符。
If the class defines the equality operator, it should also define !=. Users of the class will assume that if they can compare for equality, they can also compare for inequality. The same argument applies to the other relational operators as well. If the class defines <, then it probably should define all four relational operators (>, >=, <, and <=). 如果类定义了相等操作符,它也应该定义不等操作符 !=。类用户会假设如果可以进行相等比较,则也可以进行不等比较。同样的规则也应用于其他关系操作符。如果类定义了 <,则它可能应该定义全部的四个关系操作符(>,>=,<,<=)。 Choosing Member or Nonmember Implementation选择成员或非成员实现When designing the overloaded operators for a class, we must choose whether to make each operator a class member or an ordinary nonmember function. In some cases, the programmer has no choice; the operator must be a member. In other cases, there are some rules of thumb that can help guide the decision. The following guidelines can be of help when deciding whether to make an operator a member or an ordinary nonmember function: 为类设计重载操作符的时候,必须选择是将操作符设置为类成员还是普通非成员函数。在某些情况下,程序员没有选择,操作符必须是成员;在另一些情况下,有些经验原则可指导我们做出决定。下面是一些指导原则,有助于决定将操作符设置为类成员还是普通非成员函数:
![]() |