Практический курс по C/C++: https://stepik.org/course/193691
На прошлых
занятиях для обращения к элементам пространства имен std нам приходилось
прописывать std::cin, std::cout и так далее. Это не очень удобно. Если
в модуле (файле) предполагается постоянное использование каких-либо элементов
из того или иного пространства имен, то эти элементы можно импортировать
непосредственно в этот модуль. В частности, для этого предназначен оператор using. Использовать
его можно в соответствии со следующим синтаксисом:
using <пространство имен>::<элемент>;
Например, чтобы
импортировать объекты cin/cout и функцию endl в текущий модуль
в глобальное пространство имен, достаточно прописать:
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
int main()
{
char str[50];
cin >> str;
cout << "Hello, " << str << "!" << endl;
return 0;
}
Если же этот
оператор прописать в каком-либо блоке, например, в теле функции main(), то импорт
будет сделан в пределах этого блока:
#include <iostream>
int main()
{
using std::cout;
using std::cin;
using std::endl;
char str[50];
cin >> str;
cout << "Hello, " << str << "!" << endl;
return 0;
}
Соответственно,
определения cout, cin и endl доступны теперь
только внутри тела функции main() и не доступны за его пределами.
Обычно, на
практике, отдельные элементы импортируются в глобальную область или в
пространство имен, где их активно предполагается использовать. А вот делать
импорт всех определений того или иного пространства имен следует исключительно
в локальные области.
Давайте
посмотрим, как синтаксически выполняется импорт всего пространства. Для этого
также прописывается ключевое слово using, за которым
следует еще одно ключевое слово namespace с указанием имени импортируемого
пространства. Например:
int main()
{
using namespace std;
// using std::cout;
// using std::cin;
// using std::endl;
char str[50];
cin >> str;
cout << "Hello, " << str << "!" << endl;
return 0;
}
В результате,
все определения из std становятся доступными напрямую в теле функции main(). И, еще раз,
обратите внимание. Писать подобный импорт в глобальной области – крайне плохая
практика:
#include <iostream>
using namespace std;
int main()
{
char str[50];
cin >> str;
cout << "Hello, " << str << "!" << endl;
return 0;
}
Часто, далеко не
все, что прописано в std, используется в текущем модуле. И
программист вполне может случайно объявить переменную или функцию или что-либо
еще с тем же именем, что и импортированный элемент. Возникнет конфликт имен и компиляция
программы завершится с ошибкой. Как раз чтобы этого избежать, лучше явно
указывать импортируемые элементы:
using std::cout;
using std::cin;
using std::endl;
Или, в крайнем
случае, делать полный импорт в локальную область видимости.
Определение псевдонимов типов
Но это еще не
все, что умеет делать оператор using. С его помощью можно создавать
псевдонимы (алиасы) существующих в программе типов данных. Делается это по
следующему синтаксису:
using <alias> = <тип данных>;
Например, в
самом простом варианте, можно прописать что-то вроде:
using byte_8 = unsigned char;
В программе
появляется новое имя byte_8 базового типа unsigned char, которое
полноценно можно использовать следующим образом:
byte_8 byte;
byte_8* byte_ptr;
Или, такой
пример. Пусть у нас в программе объявлено пространство имен с определением функции
и структуры:
namespace firstSpace {
void foo()
{
cout << "function from firstSpace: foo()" << endl;
}
struct point {
double x, y;
};
}
Тогда для типа firstSpace::point можно создать
псевдоним следующим образом:
using point2D = firstSpace::point;
и использовать
его для объявления соответствующей переменной на структуру:
При этом прежний
тип firstSpace::point, конечно же продолжает существовать. Мы лишь создаем еще
одно имя этого типа не более того. И using создает
псевдонимы именно для типов данных. Например, запись вида:
using func = firstSpace::foo;
приведет к
ошибке, т.к. foo – это имя
функции, а не тип данных.
Вообще
конструкция
using <alias> = <тип данных>;
очень напоминает
оператор typedef языка Си.
Например, с его помощью мы также можем записать:
typedef unsigned char byte_8;
typedef firstSpace::point point2D;
На первый взгляд
никаких отличий. Но они все же имеются. Оператор using полностью покрывает
функциональность оператора typedef и привносит некоторые
дополнительные возможности и улучшения. Например, объявление типа указателя на
функцию через typedef выглядит так:
typedef float (*func_ptr)(int);
а с
использованием using несколько понятнее и красивее:
using func_ptr = float (*)(int);
Но, конечно,
главное преимущество using перед typedef проявляются при
работе с шаблонами (templates). Но это уже выходит за пределы нашего
базового курса по С++. Отмечу лишь, что в современных программах на С++ нет
смысла использовать typedef и от него лучше отказываться в пользу
оператора using. Хотя это не
строгое правило и применение typedef все же
допустимо.
Практический курс по C/C++: https://stepik.org/course/193691