Рейтинговые книги
Читем онлайн C++. Сборник рецептов - Д. Стефенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 34 35 36 37 38 39 40 41 42 ... 136

 s += str;

 s += " как число!";

 throw (s);

}

return (d);

Наиболее важной частью здесь является то, что все, что требуется сделать, — это использовать для чтения из строкового потока в double оператор сдвига вправо (>>), как это делается при чтении из cin.

Ну, это не совсем все, что требуется сделать. Если в stringstream записано значение, которое не может быть записано в переменную в правой части оператора >>, то для потока будет выставлен бит fail. Этот бит можно проверить с помощью функции-члена fail (на самом деле это функция-член basic_ios, который является родительским классом для stringstream). Кроме того, переменная справа от оператора >> в случае ошибки значения не меняет.

Однако с целью обобщения можно избежать написания отдельных версий sciToDub для типов int, float, double и чего-либо еще, что может потребоваться преобразовать, если написать шаблон функции. Рассмотрим такую новую версию.

template<typename T>

T strToNum(const string& str) {

 stringstream ss(str);

 T tmp;

 ss >> tmp;

 if (ss.fail()) {

  string s = "Невозможно отформатировать ";

  s += str;

  s += " как число!";

  throw (s);

 }

 return (tmp);

}

Теперь, чтобы преобразовать string в числовой тип, можно сделать так.

double d = strToNum<double>("7.0");

float f = strToNum<float>("7.0");

int i = strToNum<int>("7.0");

Также параметром шаблона можно сделать тип символов, но это очень просто сделать, так что я оставляю это в качестве вашего упражнения.

Смотри также

Рецепт 3.2.

3.6. Преобразования между числовыми типами

Проблема

Имеется число одного типа и требуется преобразовать его в другой, как int в short или наоборот, но при этом необходимо перехватывать все ошибки переполнения (overflow) или потери значимости (underflow), возникающие при работе программы.

Решение

Используйте шаблон класса numeric_cast Boost. Он выполняет проверки, которые при переполнениях переменной, принимающей значение, или других ошибках выбрасывают исключение типа bad_numeric_cast. Пример 3.8 показывает, как это выполняется.

Пример 3.8. Безопасное преобразование чисел

#include <iostream>

#include <boost/cast.hpp>

using namespace std;

using boost::numeric_cast;

using boost::bad_numeric_cast;

int main() {

 // Целые типы

 try {

  int i = 32767;

  short s = numeric_cast<short>(i);

  cout << "s = " << s << endl;

  i++; // Теперь i выходит за диапазон (если sizeof(short) равен 2)

  s = numeric__cast<short>(i);

 } catch (bad_numeric_cast& e) {

  cerr << e.what() << endl;

 }

 try {

  int i = 300;

  unsigned int ui = numeric_cast<unsigned int>(i);

  cout << ui << endl; // Прекрасно

  i *= -1;

  ui = numeric_cast<unsigned int>(i); // i отрицателен!

 } catch (bad_numeric_cast& e) {

  cerr << e.what() << endl;

 }

 try {

  double d = 3.14.

  int i = numeric_cast<int>(d);

  i = numeric_cast<int>(d); // Это отрезает 0.14!

  cout << i << endl; // i = 3

 } catch (bad_numeric_cast& e) {

  cerr << e.what( ) << endl;

 }

}

Обсуждение

Вы, вероятно, знаете, что базовые типы C++ имеют различные размеры. Стандарт C++ содержит жесткие указания по относительному размеру типов: int всегда не короче, чем short int, но он не указывает абсолютных размеров. Это означает, что если взять long int и попытаться записать его значение в short или попытаться поместить int в unsigned int, то информация о значении переменной-источника, такая как знак или даже часть числового значения, может быть потеряна.

Только знания, что это может привести к проблемам, не достаточно. Вы можете быть ограничены жесткими требованиями по объему и не захотите использовать четыре байта для long, когда можно обойтись двумя байтами для short (если ваша платформа на самом деле использует такие размеры, что очень распространено, но не гарантируется). Из-за ограничений по объему может возникнуть желание попробовать хранить значения в наименьших возможных типах. Если вы любите приключения, но вам нужна страховка, для перехвата потерь данных при работе программы используйте numeric_cast из Boost.

Синтаксис numeric_cast очень прост. Это шаблон функции, объявленный следующим образом.

template<typename Target, typename Source>

inline Target numeric_cast(Source arg)

Если вы уже прочли рецепты 3.1 и 3.3, он аналогичен lexical_cast. У него имеется два параметра шаблона — Target и Source, — которые представляют типы оригинального и результирующего значений. Так как это шаблон функции, компилятор может догадаться о типе аргумента Source, так что требуется указать только Target, как здесь.

int i = 32767;

short s = numeric_cast<short>(i);

short — это аргумент, передаваемый в шаблон как параметр Target. Компилятор догадывается, что Source имеет тип int потому, что i имеет тип int.

В этом случае я впихиваю int в short. В моей системе (Windows XP) int имеет длину четыре байта, a short — два. short имеет знак, это означает, что для представления числа в нем используется 15 бит и, следовательно, максимальным допустимым положительным значением для него является 32 767. Приведенный выше фрагмент кода работает молча, но когда я увеличиваю i на единицу, она выходит за диапазон short.

s = numeric_cast<short>(i); // Ох!

Вы уже догадались, что выбрасывается исключение bad_numeric_cast. Смотри остальную часть примера 3.8: numeric_cast также перехватывает потери знака, возникающие при присвоении отрицательного значения со знаком типу без знака.

Но numeric_cast не решает всех проблем. Если попытаться поместить значение с плавающей точкой в тип без плавающей точки, то будет потеряно все, что находится справа от десятичной точки, так? numeric_cast в этой ситуации не спасает, так что не думайте, что он сможет уберечь вас от всех рискованных предприятий. Например, рассмотрим такой фрагмент кода из примера 3.8:

double a = 3.14;

int i = numeric_cast<int>(d); // Ох!

Здесь не будет выброшено никаких исключений. Но это произойдет, если попробовать такое:

double d = -3.14;

unsigned int ui = numeric_cast<unsigned int>(d);

Потому что, несмотря на то что происходит потеря всего, что находится справа от десятичной точки, происходит потеря знака, а это очень плохо.

Смотри также

Рецепты 3.1 и 3.3.

3.7. Получение минимального и максимального значений числового типа

Проблема

Требуется узнать наибольшее и наименьшее значения, представляемые на данной платформе числовым типом, таким как int или double.

Решение

Чтобы среди прочего получить максимальное и минимальное допустимые значения числового типа, используйте шаблон класса numeric_limits из заголовочного файла <limits> (см. пример 3.9).

Пример 3.9. Получение числовых ограничений

#include <iostream>

#include <limits>

using namespace std;

template<typename T>

void showMinMax() {

 cout << "min: " << numeric_limits<T>::min() << endl;

 cout << "max: " << numeric_limits<T>::max() << endl;

 cout << endl;

}

int main() {

 cout << "short:" << endl;

 showMinMax<short>();

 cout << "int:" << endl;

 showMinMax<int>();

 cout << "long:" << endl;

 showMinMax<long>();

 cout << "float:" << endl;

 showMinMax<float>();

 cout << "double:" << endl;

 showMinMax<double>();

 cout << "long double:" << endl;

 showMinMax<long double>();

 cout << "unsigned short:" << endl;

 showMinMax<unsigned short>();

 cout << "unsigned int:" << endl;

 showMinMax<unsigned int>();

 cout << "unsigned long:" << endl;

 showMinMax<unsigned long>();

}

Вот что я получил в Windows XP, используя Visual C++ 7.1.

short:

min: -32768

max: 32767

int:

min: -2147483648

max: 2147483647

long:

min -2147483648

max 2147483647

float:

min: 1.17549e-038

max: 3.40282e-038

double:

min: 2.22507e-308

max: 1.79769e+308

1 ... 34 35 36 37 38 39 40 41 42 ... 136
На этой странице вы можете бесплатно читать книгу C++. Сборник рецептов - Д. Стефенс бесплатно.

Оставить комментарий