2016-10-21 3 views
1

Я просто пытался реализовать свою собственную функцию для строки до int преобразования. Моя функция заключается в следующем:Обработка ошибок в функции, которая преобразует строку в целое число

int func(string s) 
{ 
    if error return -1; 
    else do something and return the value 
} 

Я просто одно сомнение, что, если моя строка была s = «-1», то как бы я различать между «-1» возвращается на ошибку и для правильное преобразование строки. Спасибо за вашу помощь. Надеюсь, мой вопрос был достаточно ясным.

+8

Невозможно. Конструкция вашей функции ошибочна. – juanchopanza

+2

Почему бы просто не выбросить исключение, если строка не представляет число? – Mureinik

+4

Это показывает, почему обработка ошибок всегда сложна. В общем случае любая функция имеет два выхода: (1) значение успеха/отказа и (2) возвращаемое значение. Всегда заманчиво пытаться объединить эти два, потому что возвращение двух вещей по отдельности - это неприятность, это никогда не так удобно. Но если вы объедините два (как вы пытаетесь здесь), и если набор нормальных значений возврата не отличается от различия успеха/ошибки, вы получите неразрешимую двусмысленность. –

ответ

1

Ваша конструкция функция может быть:

int func(string s, int *out) 
{ 
    if error return -1; 
    else { 
     do something and set *out to the value; 
     return 1; 
    } 
} 

В комментарии изложили, возвращаемое значение перегружен -1, если он будет представлять как действительное значение и ошибку. Поскольку нет целочисленного значения, которое может представлять оба, требуется дополнительное возвращаемое значение. В этом дизайне возвращаемое значение функции представляет собой успех или неудачу, а параметр *out возвращает результат. Вы можете поменять местами эти два (параметр получает успех или неудачу, а возвращаемое значение - значение).

+0

Очень распространенное соглашение - вернуть '0' для успеха, отличное от нуля для ошибки. Это противоположность общему значению zero = false, nonzero = true для булевых, но может быть полезно кодировать дополнительную информацию в ненулевом коде ошибки. –

+0

@ Keith-Thompson, альтернативой является «молчащий сбой», такой как 'atoi', который возвращает' 0', также если он не может преобразовать значение. –

+0

Да, это альтернатива - также известная как * плохая идея *. –

3

У вас есть несколько вариантов с различными компромиссами.

1. Исключение. Выбросьте исключение в случае ошибки. Исключения не должны попадать непосредственно на сайт вызова, но их можно обработать в иерархии вызовов, где у вас больше контекста для устранения ошибки. Я бы даже сказал, что если вы всегда включаете вызов функции в блоки try-catch, вы можете неправильно понять преимущество исключений.

int func(const std::string& s); 

2. Дополнительное возвращаемое значение.std::optional, доступный на C++ 17 (или ранее как boost::optional или техническая спецификация (TS)). Это явно указывает, что не возвращать значение является допустимым случаем, который следует учитывать при вызове функции.

std::optional<int> func(const std::string& s); 

Отличная вещь, явный оператор BOOL, что позволяет этой идиомы:

if (std::optional<int> i = func("hello")) // or even auto 
     use(*i); 

3. Указатель возвращаемое значение. С семантикой, аналогичной предыдущей точке, это может потребоваться, когда возвращается сохраненный элемент, например. в поиске. Если он не найден, возвращается nullptr. В отличие от std::optional, это имеет тот недостаток, что для этого требуется управление памятью/пожизненным временем (либо объект гарантированно переживет вызов, либо он распределяется динамически). Никогда не возвращайте указатель, который должен быть явно удален вызывающим, если вам действительно нужно использовать динамическое распределение std::unique_ptr. Имейте в виду, что динамическое распределение дорогостоящее и почти всегда переполняется для небольших объектов, таких как int. Кроме того, вам нужно подумать о const-correctness.

int* func(const std::string& s); 
const int* func(const std::string& s); 
std::unique_ptr<int> func(std::string& s); 

4. Проверка-и-выход-параметр идиомы. Имейте функцию, которая принимает выходной параметр. Если он генерирует значение, он записывает результат в этот параметр и возвращает true, иначе он возвращает false.

bool func(int& out); 

Это позволяет делать вызовы следующим образом.Недостатком является то, что вы не можете напрямую инициализировать объект (и, следовательно, не можете использовать const -qualified, созданные по умолчанию или не назначаемые объекты, например).

int value; 
if (func(value)) 
    use(value); 

5. Утверждение. Это то, что многие люди упускают из виду, особенно те, кто приходит из таких языков, как Java, где большинство ошибок категорически обрабатываются исключениями. Если единственным случаем, когда неверное значение будет возвращено, является неправильное обращение к функции - то есть логическая ошибка (не говоря уже об ошибке) в вашей программе - тогда часто бывает целесообразнее сделать это узнаваемым как можно скорее. Отладчики немедленно остановятся при неудачных утверждениях, в то время как ваш продуктивный код будет полностью оптимизирован.

int func(const std::string& s); 

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

1

Вы можете просто выбросить исключение.

int func (string const & input) 
{ 
    if (error) 
    throw std::runtime_error("stuff happenend") 
    else 
    return value; 
} 
0

Я хотел бы использовать boost::lexical_cast и пусть вызывающий обрабатывать bad cast exceptions.

// will throw bad_lexical_cast if any issue. 
int convert(const std::string& num) { 
    return boost::lexical_cast<int>(num); 
} 


int main() 
{ 
    try { 
     std::cout << convert("1") << "\n"; 
     std::cout << convert("-1") << "\n"; 
     std::cout << convert("abc") << "\n"; 
    } catch (const boost::bad_lexical_cast& blc) { 
     std::cout << blc.what() << "\n"; 
    } 

return 0; 
} 

Это прост и хорошо протестирован.

Простой способ был бы просто использовать boost::lexical_cast<T>() без функции convert, которая дает вам только обертку;

Пробовайк here

+0

Почему бы не использовать ['std :: stoi'] (http://en.cppreference.com/w/cpp/string/basic_string/stol)? – juanchopanza

+0

@ юанчхопанза мазохизм. Это новый черный цвет. – user4581301

+0

@juanchopanza, потому что lexical_cast более гибкий с видом конверсий. [проверьте эту ссылку с более подробной информацией о lexical_cast] (http://www.boost.org/doc/libs/1_55_0/doc/html/boost_lexical_cast/performance.html#boost_lexical_cast.performance.tests_description). Он также дает вам единый интерфейс для разных типов. – Dam

Смежные вопросы