C++ также предоставляет автоматический поиск в пространствах имен, к которым относятся параметры функций. Так, например, следующий код описывает аргументы в пространстве имен (
dev
— это пространство имен, в котором объявлен
Device
):
void f(dev::Device& d) {
register(d); // на самом деле это dev::register
}
При передаче функции параметра, принадлежащего пространству имен, компилятор включает это пространство имен при поиске имен функций, вызываемых в теле этой функции. Это, может быть, не самая часто используемая
особенность, но когда она используется, то экономит значительное время набора кода и позволяет избежать лишних директив
using
. В основе этого лежит идея о том, что функции, которые оперируют с каким-либо типом, часто определяются в том же пространстве имен, что и этот тип. Кстати, вообще всегда, когда возможно, следует помещать функции, оперирующие определенными типами, в то же пространство имен, в котором находятся эти типы.
Последним интересным моментом, связанным с пространствами имен, является подстановка имен перегрузок в объявлениях
using
. Рассмотрим такой пример.
namespace mylib {
void foo(mt);
void foo(double);
void foo(std::string);
// Другие перегрузки foo...
}
// В каком-то другом файле...
using mylib::foo; // Какой вариант используется?
Объявление
using
соответствует всем перегрузкам
foo
, так что писать отдельную директиву для каждой перегрузки не требуется. Другим преимуществом этой записи является то, что если добавляется еще одна перегрузка
foo
, то весь код, содержащий объявление вида
mylib::foo
, видит ее автоматически (конечно, при компиляции кода, содержащего это объявление), так как объявление
using
включает и ее.
Конечно, использовать пространства имен следует обдуманно, а иначе у вас или тех, кто будет их использовать, появятся неожиданные ошибки компиляции. Вот несколько популярных советов по использованию пространств имен.
Как можно реже используйте
using namespace xxx
Как я объяснял ранее, импорт всего пространства имен увеличивает вероятность конфликта имен — либо сразу, либо в будущем (в используемое вами пространство имен может быть добавлено что-то, что приведет к конфликту). Это также снижает степень модульности, предоставляемую пространствами имен.
Не используйте оператор
using
в заголовочных файлах
Заголовочные файлы включаются большим количеством других файлов, так что использование пространства имен или чего-либо из пространства имен в заголовочном файле открывает его файлам, включающим этот заголовочный файл. Решение этой проблемы заключается в указании в заголовочных файлах полных имен.
Не помещайте объявления
using
или определения перед директивами
#include
Если это сделать, тогда то, что указано в директиве
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, облегчающие выполнение этих задач. Также имеется несколько рецептов по работе исключительно с числовыми типами: безопасное преобразование между ними, сравнение чисел с плавающей точкой с граничными значениями и поиск минимального и максимального значений.