Как перегрузить оператор ++ двумя разными способами для постфикса a ++ и префикса ++ a?


110

Как перегрузить оператор ++ двумя разными способами для постфикса a++и префикса ++a?



Ответы:


165

Должно получиться так:

class Number 
{
    public:
        Number& operator++ ()     // prefix ++
        {
           // Do work on this.   (increment your object here)
           return *this;
        }

        // You want to make the ++ operator work like the standard operators
        // The simple way to do this is to implement postfix in terms of prefix.
        //
        Number  operator++ (int)  // postfix ++
        {
           Number result(*this);   // make a copy for result
           ++(*this);              // Now use the prefix version to do the work
           return result;          // return the copy (the old) value.
        }
}; 

15
Этот код также показывает разницу в производительности префикса и постфикса. Если возвращаемый объект не помещается в регистр ЦП, значит, вы выполняете дорогостоящую операцию копирования. Это нормально, если вам нужно использовать предварительно увеличенное значение, но если вы этого не сделаете, постфикс намного лучше. Примером может служить итератор, в котором вы обычно используете: for (pos = c.begin (); ...; ++ pos) {} вместо pos ++
Эрик

22
@Eric: Вы все исправили, кроме предложения в середине, где вы смешиваете. Его приставка лучше.
Мартин Йорк

6
Почему Number operator++ (int)принимает intпараметр как параметр, даже если вы его не используете?
Шон

10
@SeanLetendre: на самом деле он не принимает параметр int. Это фальшивый параметр. Но разработчики языка C ++ должны были определить способ различать определения префиксных и постфиксных функций. Это дизайнерское решение, которое они приняли.
Мартин Йорк

2
@EnricoMariaDeAngelis: синтаксис различает их. ++xявляется префиксом и, следовательно, вызывает, operator++() а x++является постфиксом и, таким образом, вызываетoperator++(int)
Мартин Йорк

34

Разница заключается в том, какую сигнатуру вы выбираете для своей перегрузки (ов) operator ++ .

Цитируется из соответствующей статьи по этой теме в FAQ по C ++ (подробности см. Здесь):

class Number {
  public:
    Number& operator++ ();     // prefix ++: no parameter, returns a reference
    Number  operator++ (int);  // postfix ++: dummy parameter, returns a value
};

PS: Когда я узнал об этом, все, что я увидел изначально, это фиктивный параметр, но на самом деле различные возвращаемые типы более интересны; они могут объяснить, почему ++xсчитается более эффективным, чем x++ в целом .


17

У вас есть два способа перегрузить два оператора (префиксный / постфиксный) ++ для типа T:

Объектный метод:

Это самый простой способ, использующий «общую» идиому ООП.

class T
{
    public :
        T & operator++() // ++A
        {
            // Do increment of "this" value
            return *this ;
        }

        T operator++(int) // A++
        {
           T temp = *this ;
           // Do increment of "this" value
           return temp ;
        }
} ;

Функция объекта, не являющаяся членом:

Это еще один способ сделать это: пока функции находятся в том же пространстве имен, что и объект, на который они ссылаются, они будут учитываться, когда компилятор будет искать функцию для обработки ++t ;или t++ ;код:

class T
{
    // etc.
} ;


T & operator++(T & p_oRight) // ++A
{
   // Do increment of p_oRight value
   return p_oRight ;
}

T operator++(T & p_oRight, int) // A++
{
   T oCopy ;
   // Copy p_oRight into oCopy
   // Do increment of p_oRight value
   return oCopy ;
}

Важно помнить, что с точки зрения C ++ (включая точку зрения компилятора C ++) эти функции, не являющиеся членами, по-прежнему являются частью интерфейса T (до тех пор, пока они находятся в одном пространстве имен).

Нотация функций, не являющихся членами, дает два потенциальных преимущества:

  • Если вам удастся закодировать их, не сделав их друзьями T, тогда вы увеличите инкапсуляцию T
  • вы можете применить это даже к классам или структурам, код которых вам не принадлежит. Это ненавязчивый способ улучшить интерфейс объекта без изменения его объявления.

1

Объявить так:

class A
{
public:
    A& operator++();    //Prefix (++a)
    A operator++(int); //Postfix (a++)

};

Реализуйте правильно - не связывайтесь с тем, что все знают, что они делают (увеличивайте, затем используйте, используйте, затем увеличивайте).


-2

Я знаю, что уже поздно, но у меня была та же проблема, и я нашел более простое решение. Не поймите меня неправильно, это то же самое решение, что и верхнее (опубликовано Мартином Йорком). Это просто немного проще. Самую малость. Вот:

class Number
{
        public:

              /*prefix*/  
        Number& operator++ ()
        {
            /*Do stuff */
            return *this;
        }

            /*postfix*/
        Number& operator++ (int) 
        {
            ++(*this); //using the prefix operator from before
            return *this;
        }
};

Вышеупомянутое решение немного проще, потому что оно не использует временный объект в методе постфикса.


6
Это не стандартно. Постфиксный оператор ++ должен возвращать то значение, которое было до увеличения, а не после.
Kuilin Li

Это неверный ответ. Требуется временный.
Риан Куинн
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.