Для правильного кода, вам необходимо поддерживать инварианты структуры данных с каждой операцией. Путь исходный код написан, инварианты выглядеть следующим образом:
- A1:
front
указывает на первый элемент, если он существует; NULL
В противном случае
- A2:
rear
указывает на последний элемент, если он существует; NULL
иначе
Вы утверждаете, что инварианты в качестве альтернативы может быть этим:
- B1:
front
указывает на первый элемент, если он существует; NULL
иначе
- B2:
rear
указывает на последний элемент, если он существует
Теперь они похожи, но не идентичны. Инверторы B не требуют никакого специального значения rear
, когда список пуст. Теперь вы можете использовать инварианты B для реализации списка, конечно, поскольку он допускает дискриминацию между пустым и непустым состоянием, и этого достаточно, чтобы обеспечить правильные реализации.
Но это ничего не говорит о том, лучше ли A или B на практике. Если вы используете инварианты B, функция заглянуть в последний элемент не может просто вернуть значение rear
, так как его значение может быть любым, если очередь пуста; вы должны сначала проверить значение front
.
// for the A2 invariant
return rear ;
// for the B2 invariant
return (front == NULL) ? NULL : rear ;
Это сводится к тому, стоит ли хотеть, чтобы проверить-и-Assign, когда вы или из очереди ли хотите, чтобы проверить, когда вы взглянуть на последний элемент. Это вопрос оптимизации, а не правильный. Если вам никогда не нужно заглядывать в последний элемент, вы можете оптимизировать для этого.
Все это говорит о том, что это главный случай для опасностей преждевременной оптимизации. Дополнительное назначение указателя на NULL
почти никогда не будет проблемой производительности. Скорее всего, проблема заключается в введении дефекта во время обслуживания, когда кто-то полагается на инварианты А, когда существующий код использует B. А инварианты A когнитивно проще, потому что front
и rear
настроены аналогично, причем не имеют особого поведения. Инварианты B могли бы работать лучше, но ценой сложности. Любой выбор может быть лучше в зависимости от ваших обстоятельств.
Мораль этой истории: Всегда документируйте свои инварианты.
Можете ли вы дать объяснение важности документирования инвариантов? –
Я бы подумал, что ответ уже предусматривает это. Фрагмент кода предоставляет альтернативные реализации функции, предполагающей различные инварианты. Если вы пишете не тот, вы написали дефектный код. Всегда существует несколько способов реализации любого данного класса, но эти разные способы (обычно) не взаимно совместимы. Если вы не укажете инварианты, вы можете легко неправильно смешивать фрагменты реализации, которые являются правильными сами по себе, но не корректны вместе. – eh9