#cpp #сеттеры
Допустим, у меня есть такой сеттер. И всё ок, если я передам в него значение, которое пройдёт проверку. А если пользователь введёт, например, 301? Надо ведь тогда повторно переспросить, сообщить об ошибке. Но сеттер ничего не возвращает. Не понимаю - как поставить условие для "переспрашивания" или дальнейшего выполнения кода? void Player::SetAge(const int newAge) { if (newAge < 0 || newAge > 300) return; age = newAge; }
Ответы
Ответ 1
Можно возвращать bool, показывающий, успешно ли выполнено присваивание - это скорее сишный стиль, когда вместо исключений использовались коды возврата: bool Player::SetAge(const int newAge) { if (newAge < 0 || newAge > 300) return false; age = newAge; return true; } А можно кидать исключение - это стиль си++ и большинства современных языков: void Player::SetAge(const int newAge) { if (newAge < 0 || newAge > 300) throw std::invalid_argument("Age should be between 0 and 300 inclusive"); age = newAge; }Ответ 2
Текст ниже касается не только сеттеров но и их тоже. Здесь от логики вашего приложения зависит. Если по вашей задумке плохие параметры в сетер не будут передаваться никогда, но боитесь багов, стоит использовать assert. void Player::SetAge(const int newAge) { assert(newAge >= 0 && newAge <= 300); age = newAge; } При дебажной сборке, если условие нарушится, программа в этом месте упадёт, и через дебагер можно просмотреть что пошло не так. Удобно дебажить и тестить. В релизной сборке эта проверка игнорируется, что положительно сказывается на скорости. Если же вы предполагаете, что данные могут быть не валидными и хотите корректно обрабатывать эту ситуацию есть 2 подхода. Первый - использовать код возврата. int Player::SetAge(const int newAge) { if (newAge < 0 || newAge > 300) return -1; age = newAge; return 1; } Вместо int, можно bool или что угодно другое использовать. Обычно используют int и для каждой причины ошибки свой номер. Второй - пробрасывать исключения. void Player::SetAge(const int newAge) { if (newAge < 0 || newAge > 300) throw std::runtime_error("error text"); age = newAge; } Часто используют собственные исключения вместо стандартных. Мешать в одном проэкте эти 2 подхода не стоит. Первый (код возврата) чуть быстрее работает, но, если есть ресурсы, которые нужно освобождать вручную (закрытие файла например, или освободить мьютекс), нужно писать это перед каждым return. Стоит такие классы не использовать (и даже если решили не через код возврата, а через исключения писать). Исключения же немного удобнее. Во первых нет необходимости для каждой функции проверять код возврата. Типичный код на кодах возврата выглядит: { шf (Foo1(...) != RESULT_OK ) { return (ERROR); } шf (Foo2(...) != RESULT_OK ) { return (ERROR); } шf (Foo3(...) != RESULT_OK ) { return (ERROR); } шf (Foo3(...) != RESULT_OK ) { return (ERROR); } return RESULT_OK; } Во вторых во время дебага видно нормальную ошибку с нормальным текстом, поддерживается наследование и многое другое. Си не поддерживает исключения, там только коды вызврата. В плюсовом коде обычно практикуют писать через исключения. Stl написан через исключения. Коды возврата в плюсовом коде используют реже, но используют, в гугле, например.Ответ 3
Сеттер должен сохранять значение, а не завершать программу, бросать исключение и т.п. Делайте валидацию отдельно, в сеттере как максимум напишите assert(IsValidAge(newAge));Ответ 4
Всем спасибо! Почитав ваши ответы, пока остановилась на варианте такого типа: сам метод bool Rectangles::SetColor(int new_colorOfBackground) { if (new_colorOfBackground >= 0 && new_colorOfBackground <= 16) { colorOfBackground = new_colorOfBackground; HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hStdOut, (WORD)((colorOfBackground << 4) | WHITE)); return true; } else { cout << "Error. Number of color must be from 1 to 16." << endl; return false; } } мейн: (int main() { int new_colorOfBackground = 0; Rectangles R; do { cout << "Choose the color (from 0 to 16): "; cin >> new_colorOfBackground; } while (R.SetColor(new_colorOfBackground) == false); //(...) system("pause"); return 0; }Ответ 5
Беззнаковый тип поможет проверять только одно условие, а дальше по вашему усмотрению: void Player::SetAge(const unsigned newAge) { if (newAge <= 300) age = newAge; //в обратном случаи, если нужно, делаем что то //else some_function(); }
Комментариев нет:
Отправить комментарий