C++. Сборник рецептов - Д. Стефенс
Шрифт:
Интервал:
Закладка:
Если это сделать, тогда то, что указано в директиве using, будет открыто для кода заголовочного файла, что, вероятно, не входило в намерения автора этого заголовочного файла.
При выполнении этих правил использование пространств имен в новом проекте или добавление их в существующий проект должно быть относительно просто.
2.5. Включение встраиваемого файла
ПроблемаИмеется несколько функций-членов или самостоятельных функций, которые требуется сделать встраиваемыми (inline), но вы не хотите определять их все в определении класса (или даже после него) в заголовочном файле. Это позволит хранить объявление и реализацию по отдельности.
РешениеСоздайте файл .inl и с помощью #include включите его в конец своего заголовочного файла. Это эквивалентно помещению определения функции в конец заголовочного файла, но позволяет хранить объявление и определение раздельно. Пример 2.6 показывает, как это делается.
Пример 2.6. Использование встраиваемого файла
// Value.h
#ifndef VALUE_H__
#define VALUE_H__
#include <string>
class Value {
public:
Value (const std::string& val) : val_(val) {}
std::string getVal() const;
private:
std::string val_;
};
#include "Value.inl"
#endif VALUE_H__
// Value.inl
inline std::string Value::getVal() const {
return(val_);
}
Это решение не требует пояснений, #include заменяется на содержимое ее аргумента, так что здесь в заголовочный файл включается содержимое Value.inl. Следовательно, любой файл, включающий этот заголовочный файл, содержит определения встраиваемых функций, но вам не требуется загромождать объявление класса.
Глава 3
Числа
3.0. Введение
Даже если вы не занимаетесь написанием научных или инженерных приложений, вам все равно придется работать с числами. Эта глава содержит решения проблем, часто возникающих при работе с числовыми типами С++.
Некоторые из рецептов содержат методики преобразования из числовых типов в тип string и обратно чисел, представленных в различных форматах (шестнадцатеричном, с плавающей точкой или экспоненциальном). Самостоятельное написание кода для таких преобразований утомительно и требует времени, так что я показываю возможности стандартной библиотеки или одной из библиотек Boost, облегчающие выполнение этих задач. Также имеется несколько рецептов по работе исключительно с числовыми типами: безопасное преобразование между ними, сравнение чисел с плавающей точкой с граничными значениями и поиск минимального и максимального значений.
Рецепты в этой главе предоставляют решения некоторых общих проблем, с которыми обычно сталкиваются при работе с числами в С++, но они не пытаются решать проблем, специфичных для конкретных приложений. При написании научного или инженерного приложения вам также следует взглянуть на главу 11, которая содержит рецепты ко многим общим научным и инженерным алгоритмам.
3.1. Преобразование строки в числовой тип
ПроблемаИмеются числа в строковом формате, и вам требуется преобразовать их в числовой тип, такой как int или float.
РешениеЭто можно сделать двумя способами — с помощью функций стандартной библиотеки или с помощью класса lexical_cast из Boost (написанного Кевлином Хенни (Kevlin Henney) Функции стандартной библиотеки неуклюжи и небезопасны, но они стандартны, и в некоторых случаях потребуются именно они, так что в первом решении я представлю именно их. lexical_cast более безопасен, проще в использовании и интереснее, так что я представляю его в обсуждении.
Функции strtol, strtod и strtoul, определенные в <cstdlib>, преобразуют символьные строки, ограниченные нулем, в long int, double или unsigned long. Они могут использоваться для преобразования чисел, представленных в виде строк с любым основанием, в числовые типы. Код примера 3.1 демонстрирует функцию hex2int, которая предназначена для преобразования шестнадцатиричной строки в long.
Пример 3.1. Преобразование числовых строк в числа
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
long hex2int(const string& hexStr) {
char *offset;
if (hexStr.length( ) > 2) {
if (hexStr[0] == '0' && hexStr[1] == 'x') {
return strtol(hexStr.c_str(), &offset, 0);
}
}
return strtol(hexStr.c_str( ), &offset, 16);
}
int main() {
string str1 = "0x12AB";
cout << hex2int(str1) << endl;
string str2 = "12AB";
cout << hex2int(str2) << endl;
string str3 = "0AFG";
cout << hex2int(str3) << endl;
}
Вот вывод этой программы.
4779
4779
0
Первые две строки содержат шестнадцатеричное число 12AB. Первая из них содержит префикс 0x, а вторая — нет. Третья строка не содержит правильного шестнадцатеричного числа. В этом случае функция просто возвращает 0.
ОбсуждениеНекоторые люди склонны писать свои собственные функции для преобразования шестнадцатеричных чисел в целочисленные форматы. Но зачем изобретать колесо? Стандартная библиотека уже предоставляет эту функциональность. Пример 3.1 представляет собой функцию-оболочку, упрощающую вызов strtol. Функция strtol — это старая функция библиотеки С, и она требует от вас передачи указателя на завершающуюся нулем строку, а так же адрес еще одного указателя на строку. Этот второй указатель получает адрес, на котором обработка строки завершилась. Однако в C++ большинство людей предпочитает работать с более мощным классом string, а не со старыми указателями на символьные строки. Поэтому функция hex2int принимает параметр типа string.
Функция strtol несколько странна в том, что она позволяет использовать два разных метода указания основания 16: 16 можно передать как третий параметр функции, а можно в качестве основания передать 0, но предварить строку символами 0x (точно также, как это делается для обозначения шестнадцатеричных чисел в коде, но только помните, что в случае с strtol передается строка).
Пример 3.1 позволяет использовать оба метода. При передаче строки вида 0x12AB функция обнаружит 0x и передаст ее непосредственно в strtol, в качестве третьего параметра передав 0. В противном случае функция передаст строку, в качестве третьего параметра передав 16.
strtol и strtoul работают одинаково, за исключением типа возвращаемого значения. strtod аналогична им, но не позволяет указывать основание.
Эти старые функции С не являются единственным способом преобразования строк в числа. Проект Boost предоставляет класс преобразования lexical_cast, который выполняет то же самое для числовых строк, записанных с основанием 10. Пример 3.2 показывает как он используется.
Пример 3.2. Использование lexical_cast
#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>
using namespace std;
int main() {
string str1 = "750" ;
string str2 = "2.71";
string str3 = "0x7FFF";
try {
cout << boost::lexical_cast<int>(str1) << endl;
cout << boost::lexical_cast<double>(str2) << endl;
cout << boost::lexical_cast<int>(str3) << endl;
} catch (boost::bad_lexical_cast& e) {
cerr << "Bad cast: " << e.what() << endl;
}
}
Вывод примера 3.2 таков.
750
2.71
Bad cast: bad lexical cast: source type value could not be
interpreted as target
(Неверное преобразование: неверное лексическое преобразование: значение исходного типа не может быть преобразовано в целевой.)
Вы ведите, что для последнего значения, представляющего собой шестнадцатеричное число, он выбрасывает исключение. При преобразовании чисел с основанием, отличным от 10, требуется использовать функции strtol.
Также имеются версии функций strtol для работы с «широкими» символами. Эквивалент strtol для работы с широкими символами — это wcstol, которая объявлена в <cwchar>. Эквивалентами функций strtod и strtoul являются wcstod и wcstoul. Каждая из этих функций точно такая же, за исключением того, что те параметры, которые в функциях для узких символов имеют тип char*, в функциях для широких символов имеют тип wchar_t*.
Смотри такжеРецепт 3.2.
3.2. Преобразование чисел в строки