Как защитить пользователя, если не может вернуть nullptr для пустого стека? - PullRequest
2 голосов
/ 08 марта 2020

Я пытаюсь закодировать реализацию примера Cube, Stack, приведенного в этом примере курса Coursera из Ханойских башен , чтобы узнать больше о C ++.

В stack.h Я должен реализовать:

class Stack {
public:
void push_back(const Cube & cube);
Cube removeTop();
Cube & peekTop();
unsigned size() const;

friend std::ostream & operator<<(std::ostream & os, const Stack & stack);

private:
std::vector<Cube> cubes_;
};

У меня проблема с removeTop(). Я думал о возвращении nullptr, если стек (вектор) пуст, потому что поведение pop_back не определено для пустого вектора.

Вызов pop_back для пустого контейнера не определен. Cpp Ссылка

inline Cube Stack::removeTop() {
  if (!cubes_.empty()) {
    Cube top_cube = cubes_.back();
    cubes_.pop_back();
    return top_cube;
  }
  else {
    return nullptr;
  }
}

Однако при компиляции возникает ошибка.

./stack.h:35:12: error: no viable conversion from returned value of type
      'std::__1::nullptr_t' to function return type 'uiuc::Cube'
    return nullptr;

Как защитить пользователя, если я не могу вернуть nullptr? Я ограничен только тем, чтобы сказать пользователю, что функция не должна вызываться в пустом стеке, и позволить ему / ей позаботиться о проверке?

Ответы [ 4 ]

4 голосов
/ 08 марта 2020

Это именно то, для чего нужны исключения:

if (cubes_.empty())
    throw std::runtime_error("stack underflow");
Cube top_cube = cubes_.back();
cubes_.pop_back();
return top_cube;

Сложность этого с std::optional почти наверняка не правильный ответ здесь. Попытка выскочить из пустого стека означает, что программа заблудилась. Это должно быть серьезной ошибкой, а не тем, что маскируется интерфейсом, который говорит: «Вы можете иметь или не иметь этого, пожалуйста, проверьте позже».

2 голосов
/ 08 марта 2020

Основываясь на сигнатуре функции, вы должны реализовать, вы действительно не можете. Как правило, вы бы защищали такого рода вещи с утверждением. В некоторых ситуациях вы можете использовать шаблон NullObject или вернуть нежелательный объект. В новых версиях C ++ вы также можете использовать std::optional<T>.

inline Cube Stack::removeTop() {
  if (!cubes_.empty()) {
    Cube top_cube = cubes_.back();
    cubes_.pop_back();
    return top_cube;
  }
  else {
    return Cube {};
  }
}
2 голосов
/ 08 марта 2020

Может быть так:

inline bool Stack::removeTop(Cube& top_cube) {
  if (!cubes_.empty()) {
    top_cube = cubes_.back();
    cubes_.pop_back();
    return true;
  }
  else {
    return false;
  }
}
0 голосов
/ 08 марта 2020

Как отмечалось pyj , так как в c ++ 17 появился новый механизм для этого (принятие boost :: необязательной идеи). Это std :: необязательный контейнер. При использовании std :: необязательный вы сообщаете пользователю этой функции, что он может получить пустой ответ , и поэтому он должен проверить, инициализирован он или нет.

Из ссылки cpp:

Шаблон класса std :: option управляет необязательным вложенным значением, то есть значением, которое может присутствовать или не присутствовать.

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

Теперь для использования:

inline std::optional<Cube> Stack::removeTop() 
{
    if (!cubes_.empty()) 
    {
        Cube top_cube = cubes_.back();
        cubes_.pop_back();
        return top_cube;
    }
    else 
    {
        return std::nullopt;
    }
}

И, пока вы получаете std :: необязательный, а не Cube, выделение памяти отсутствует в куче, что является большим преимуществом по сравнению с использованием указателей для одного и того же вывода.

Если необязательный параметр содержит значение, это значение гарантированно будет выделено как часть необязательного следа объекта, т.е. Dynami c выделение памяти когда-либо происходит. Таким образом, необязательный объект моделирует объект, а не указатель, даже если определены операторы * () и оператор -> ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...