Как работает эта рекурсивная перегрузка оператора вставки (operator<<)?

#c #recursion #operator-overloading

#c #рекурсия #оператор-перегрузка

Вопрос:

Я нахожусь в процессе изучения рекурсии. Ниже приведена рекурсивная перегрузка оператора вставки для класса, который предоставляет связанный список целых чисел. Он компилируется и запускается, но я не понимаю, почему.

Я понимаю, что при перегрузке оператора вставки вы обычно возвращаете ostream ссылку, чтобы вызовы могли быть объединены в цепочку. Однако не будет ли эта функция оценивать что-то вроде out << node , затем to out << out << node , а затем to out << out << out << node и т. Д.? При достижении базового варианта и начале возврата кажется, что вы пытаетесь вставить an ostream в an ostream , что должно вызвать ошибку, не так ли?

 ostream amp; operator<<(ostream amp;out, const IntList amp;intList) { 
   if (intList.head != nullptr) out << intList.head;
   return out;
}

ostream amp; operator<<(ostream amp;out, IntNode *node) { 
   if (node->next == nullptr) {
      out << node->value;
      return out;
   }
   else { 
      out << node->value << ' ';
      node = node->next;
      return out << node;
   }
}
 

Ответ №1:

похоже, вы пытаетесь вставить ostream в ostream

Нет. Ваш << оператор возвращает an ostream , но это не означает, что вы вставляете его в другой ostream .

каждый шаг, который вы выполняете в рекурсивной функции, вы вставляете что-то в ostream и возвращаете тот же ostream. См.:

 out << node->value;
...
out << node->value << ' ';
 

вы всегда вставляете некоторое значение в ostream.

это return out << node; означает, что вы вставите node->значение в ostream и перейдете к следующему узлу (если есть следующий узел).

Для лучшего понимания, здесь итеративный метод, он должен работать точно так же, как ваш рекурсивный:

 ostream amp; operator<<(ostream amp;out, const IntList amp;intList) { 
    IntNode *node = intList.head;
    
    while(node->next != nullptr){
        out << node->value << ' ';
        node = node->next;
    }
    out << node->value;
    return out;
}
 

Комментарии:

1. Спасибо, думаю, теперь я все понял. Я думал о return out << node; операторе с двумя функциями — одной рекурсивной (<< узел) и одной not (out) — поэтому после того, как последний узел вернет out , следующая функция в стеке разрешится на out << out etc . На самом деле, это эквивалентно return operator<<(out, node); , поэтому, когда конечный узел возвращает out , это приведет к тому, что все предыдущие вызовы функций будут возвращать out , а возврат amp;out из рекурсивной перегрузки функции оператора вставки позволит связать операторы вставки в «внешний» оператор вставки

2. В вашем итеративном примере оператор в цикле while разрешит from out << node->value << ' '; to out << ' '; и, наконец, ссылку на out; , которая будет отброшена перед следующей итерацией?

3. Я исправил итерационный метод, поскольку заметил ошибку. он должен пройти через весь список, вставляя значение каждого узла и пробел. наконец, вставьте значение последнего узла и верните ostream

4. очевидно, вы должны сначала проверить это: IntList.head != nullptr