Team LiB
Previous Section Next Section

14.7. Increment and Decrement Operators

14.7. 自增操作符和自减操作符

The increment (++) and decrement (--) operators are most often implemented for classes, such as iterators, that provide pointer like behavior on the elements of a sequence. As an example, we might define a class that points to an array and provides checked access to elements in that array. Ideally, our checked-pointer class could be used on arrays of any type, which we'll learn how to do in Chapter 16 when we cover class templates. For now, our class will handle arrays of ints:

自增(++)和自减(--)操作符经常由诸如迭代器这样的类实现,这样的类提供类似于指针的行为来访问序列中的元素。例如,可以定义一个类,该类指向一个数组并为该数组中的元素提供访问检查。理想情况下,带访问检查的指针类可用于任意类型的数组,这一点的实现我们将在第十六章介绍类模板时学习。现在,我们的类将处理 int 数组:

     /*
      * smart pointer: Checks access to elements throws an out_of_range
      *                exception if attempt to access a nonexistent element
      * users allocate and free the array
      */
     class CheckedPtr {
     public:
         // no default constructor; CheckedPtrs must be bound to an object
         CheckedPtr(int *b, int *e): beg(b), end(e), curr(b) { }
         // dereference and increment operations
     private:
         int* beg;   // pointer to beginning of the array
         int* end;   // one past the end of the array
         int* curr;  // current position within the array
     };

Like ScreenPtr, this class has no default constructor. We must supply pointers to an array when we create a CheckedPtr. A CheckedPtr has three data members: beg, which points to the first element in the array; end, which points one past the end of the array; and curr, which points to the array element to which this CheckedPtr object currently refers.

ScreenPtr 一样,这个类没有默认构造函数。创建一个 CheckedPtr 对象时,必须提供指向数组的指针。一个 CheckedPtr 对象有三个数据成员:beg,指向数组的第一个元素;end,指向数组的末端;curr,指向 CheckedPtr 对象当前引用的数组元素。

The constructor takes two pointers: one pointing to the beginning of the array and the other one past the end of the array. The constructor initializes beg and end from these pointers and initializes curr to point to the first element.

构造函数的参数是两个指针:一个指向数组的开始,另一个指向数组的末端。构造函数用这两个指针初始化 begend,并将 curr 初始化为指向第一个元素。

Defining the Increment/Decrement Operators

定义自增/自减操作符

There is no language requirement that the increment or decrement operators be made members of the class. However, because these operators change the state of the object on which they operate, our preference is to make them members.

C++ 语言不要求自增操作符或自减操作符一定作为类的成员,但是,因为这些操作符改变操作对象的状态,所以更倾向于将它们作为成员。



Before we can define the overloaded increment and decrement operators for CheckedPtr, we must think about one more thing. For the built-in types, there are both prefix and postfix versions of the increment and decrement operators. Not surprisingly, we can define both the prefix and postfix instances of these operators for our own classes as well. We'll look at the prefix versions first and then implement the postfix ones.

在为类定义重载的自增操作符和自减操作符之前,还必须考虑另一件事情。对内置类型而言,自增操作符和自减操作符有前缀和后缀两种形式。毫不奇怪,也可以为我们自己的类定义自增操作符和自减操作符的前缀和后缀实例。我们首先介绍前缀形式,然后实现后缀形式。

Defining Prefix Increment/Decrement Operators

定义前自增/前自减操作符

The declarations for the prefix operators look as one might expect:

前缀式操作符的声明看起来像这样:

     class CheckedPtr {
     public:
         CheckedPtr& operator++();        // prefix operators
         CheckedPtr& operator--();
         // other members as before
      };

For consistency with the built-in operators, the prefix operations should return a reference to the incremented or decremented object.

为了与内置类型一致,前缀式操作符应返回被增量或减量对象的引用。



This increment operator ensures that the user can't increment past the end of the array by checking curr against end. We throw an out_of_range exception if the increment would move curr past end; otherwise, we increment curr and return a reference to the object:

这个自增操作符根据 end 检查 curr,从而确保用户不能将 curr 增量到超过数组的末端。如果 curr 增量到超过 end,就抛出一个 out_of_range 异常;否则,将 curr 加 1 并返回对象引用:

     // prefix: return reference to incremented/decremented object
     CheckedPtr& CheckedPtr::operator++()
     {
         if (curr == end)
             throw out_of_range
                   ("increment past the end of CheckedPtr");
         ++curr;                // advance current state
         return *this;
     }

The decrement operator behaves similarly, except that it decrements curr and checks whether the decrement would move curr past beg:

除了将 curr 减 1 并检查是否会减到 beg,自减操作符的行为与自增操作符类似:

     CheckedPtr& CheckedPtr::operator--()
     {
         if (curr == beg)
             throw out_of_range
               ("decrement past the beginning of CheckedPtr");
         --curr;              // move current state back one element
         return *this;
     }

Differentiating Prefix and Postfix Operators

区别操作符的前缀和后缀形式

There is one problem with defining both the prefix and postfix operators: They each take the same number and type of parameters. Normal overloading cannot distinguish between whether the operator we're defining is the prefix version or the postfix.

同时定义前缀式操作符和后缀式操作符存在一个问题:它们的形参数目和类型相同,普通重载不能区别所定义的前缀式操作符还是后缀式操作符。

To solve this problem, the postfix operator functions take an extra (unused) parameter of type int. When we use the postfix operator, the compiler supplies 0 as the argument for this parameter. Although our postfix function could use this extra parameter, it usually should not. That parameter is not needed for the work normally performed by a postfix operator. Its sole purpose is to distinguish the definition of the postfix function from the prefix version.

为了解决这一问题,后缀式操作符函数接受一个额外的(即,无用的)int 型形参。使用后缀式操作符进,编译器提供 0 作为这个形参的实参。尽管我们的前缀式操作符函数可以使用这个额外的形参,但通常不应该这样做。那个形参不是后缀式操作符的正常工作所需要的,它的唯一目的是使后缀函数与前缀函数区别开来。

Defining the Postfix Operators

定义后缀式操作符

We can now add the postfix operators to CheckedPtr:

现在将后缀式操作符加到 CheckedPtr

     class CheckedPtr {
     public:
         // increment and decrement
         CheckedPtr operator++(int);       // postfix operators
         CheckedPtr operator--(int);
         // other members as before
     };

For consistency with the built-in operators, the postfix operators should return the old (unincremented or undecremented) value. That value is returned as a value, not a reference.

为了与内置操作符一致,后缀式操作符应返回旧值(即,尚未自增或自减的值),并且,应作为值返回,而不是返回引用。



The postfix operators might be implemented as follows:

后缀式操作符可以这样实现:

     // postfix: increment/decrement object but return unchanged value
     CheckedPtr CheckedPtr::operator++(int)
     {

         // no check needed here, the call to prefix increment will do the check
         CheckedPtr ret(*this);        // save current value
         ++*this;                      // advance one element, checking the increment
         return ret;                   // return saved state
     }
     CheckedPtr CheckedPtr::operator--(int)
     {
         // no check needed here, the call to prefix decrement will do the check
         CheckedPtr ret(*this);  // save current value
         --*this;                // move backward one element and check
         return ret;             // return saved state
      }

The postfix versions are a bit more involved than the prefix operators. They have to remember the current state of the object before incrementing the object. These operators define a local CheckedPtr, which is initialized as a copy of *this that is, ret is a copy of the current state of this object.

操作符的后缀式比前缀式复杂一点,必须记住对象在加 1/减 1 之前的当前状态。这些操作符定义了一个局部 CheckedPtr 对象,将它初始化为 *this 的副本,即 ret 是这个对象当前状态的副本。

Having kept a copy of the current state, the operator calls its own prefix operator to do the increment or decrement, respectively:

保存了当前状态的副本后,操作符调用自己的前缀式操作符分别进行加 1 或减 1:

     ++*this

calls the CheckedPtr prefix increment operator on this object. That operator checks that the increment is safe and either increments curr or throws an exception. Assuming no exception was thrown, the postfix function completes by returning the stored copy in ret. Thus, after the return, the object itself has been advanced, but the value returned reflects the original, unincremented value.

调用这个对象的 CheckedPtr 前缀自增操作符,该操作符检查自增是否安全并将 curr 加 1 或抛出一个异常。假定不抛出异常,前自增操作符函数以返回存储在 ret 的副本而结束。因此,返回之后,对象本身加了 1,但返回的是尚未自增的原值。

Because these operators are implemented by calling the prefix versions, there is no need to check that the curr is in range. That check, and the throw if necessary, is done inside the corresponding prefix operator.

因为通过调用前缀式版本实现这些操作符,不需要检查 curr 是否在范围之内,那个检查以及必要的 throw,在相应的前缀式操作符中完成。

The int parameter is not used, so we do not give it a name.

因为不使用 int 形参,所以没有对其命名。



Calling the Postfix Operators Explicitly

显式调用前缀式操作符

As we saw on page 509, we can explicitly call an overloaded operator rather than using it as an operator in an expression. If we want to call the postfix version using a function call, then we must pass a value for the integer argument:

正如在第 14.1 节所见,可以显式调用重载操作符而不是将它作为操作符用在表达式中。如果想要使用函数调用来调用后缀式操作符,必须给出一个整型实参值:

     CheckedPtr parr(ia, ia + size);        // iapoints to an array of ints
     parr.operator++(0);                    // call postfix operator++
     parr.operator++();                     // call prefix operator++

The value passed usually is ignored but is necessary to alert the compiler that the postfix version is desired.

所传递的值通常被忽略,但该值是必要的,用于通知编译器需要的是后缀式版本。

Ordinarily it is best to define both the prefix and postfix versions. Classes that define only the prefix version or only the postfix version will surprise users who are accustomed to being able to use either form.

一般而言,最好前缀式和后缀式都定义。只定义前缀式或只定义后缀式的类,将会让习惯于使用两种形式的用户感到奇怪。



Exercises Section 14.7

Exercise 14.23:

The class CheckedPtr represents a pointer that points to an array of ints. Define an overloaded subscript and dereference for this class. Have the operator ensure that the CheckedPtr is valid: It should not be possible to dereference or index one past the end of the array.

CheckedPtr 类表示指向数组的指针。为该类重载下标操作符和解引用操作符。使操作符确保 CheckedPtr 有效:它应该不可能对超出数组末端的元素进行解引用或索引。

Exercise 14.24:

Should the dereference or subscript operators defined in the previous exercise also check whether an attempt is being made to dereference or index one before the beginning of the array? If not, why not? If so, why?

上题中定义的解引用操作符或下标操作符,是否也应该检查对数组起点之前的元素进行的解引用或索引?解释你的答案。

Exercise 14.25:

To behave like a pointer to an array, our CheckedPtr class should implement the equality and relational operators to determine whether two CheckedPtrs are equal, or whether one is less-than another, and so on. Add these operations to the CheckedPtr class.

为了表现得像数组指针,CheckedPtr 类应实现相等和关系操作符,以便确定两个 CheckedPtr 对象是否相等,或者一个小于另一个,诸如此类。为 CheckedPtr 类增加这些操作。

Exercise 14.26:

Define addition and subtraction for ScreenPtr so that these operators implement pointer arithmetic (Section 4.2.4, p. 123).

ScreenPtr 类定义加法或减法,以便这些操作符实现指针运算(第 4.2.4 节)。

Exercise 14.27:

Discuss the pros and cons of allowing an empty array argument to the CheckedPtr constructor.

讨论允许将空数组实参传给 CheckedPtr 构造函数的优缺点。

Exercise 14.28:

We did not define a const version of the increment and decrement operators. Why?

没有定义自增和自减操作符的 const 版本,为什么?

Exercise 14.29:

We also didn't implement arrow. Why?

我们也没有实现箭头操作符,为什么?

Exercise 14.30:

Define a version of CheckedPtr that holds an array of Screens. Implement the overloaded increment, decrement, dereference, and arrow operators for this class.

定义一个 CheckedPtr 版本,保存 Screen 数组。为该类实现重载的自增、自减、解引用、箭头等操作符。


Team LiB
Previous Section Next Section