Last Updated :
22 Jul, 2024
The using keyword in C++ is a tool that allows developers to specify the use of a particular namespace. This is especially useful when working with large codebases or libraries where there may be many different namespaces in use. The using keyword can be used to specify the use of a single namespace, or multiple namespaces can be listed using the using keyword.
When using the using keyword, it is important to keep in mind that the specified namespace will take precedence over any other namespace that is in scope. This can lead to unexpected results if the code is not well-organized. For this reason, it is generally considered good practice to use the using keyword sparingly and only when absolutely necessary.
In addition to the using keyword, C++ also provides the using namespace directive. This directive can be used to specify the use of all namespaces in a particular library or codebase. The using namespace directive should be used with caution, as it can make code difficult to read and maintain. This can be especially useful when working with large codebases or when you want to make sure that your code will run in a specific environment.
Use of «using» keyword in C++ STL
The using keyword can be used in the following ways:
- Using for namespaces
- Using for inheritance
- Using for aliasing
- Using for directives
1. «using» for namespaces
Using allows you to specify that you want to use a particular namespace. This is useful if you want to avoid typing out the full namespace name every time you want to use something from that namespace.
Example:
C++
#include <iostream> using namespace std; int main() { cout << "Hello, world!"; return 0; }
2. «using» for inheritance
Using allows you to specify that a class inherits from another class. This is useful if you want to avoid having to type out the full name of the base class every time you want to use it.
Example:
C++
#include <iostream> using namespace std; class Base { public: int x; // parameterized constructor Base(int a) : x(a){}; }; class Derived : public Base { public: int y; using Base::Base; }; int main() { Derived d(42); d.y = 12; cout << d.x << " " << d.y << '\n'; return 0; }
Note: The C++ compiler does not create any default and copy constructor once we explicitly define any constructor. So we need to declare them explicitly to avoid bugs.
3. «using» for aliasing:
Using allows you to specify an alternate name for a type. This can be useful if you want to avoid having to type out the full name of a type every time you want to use it.
Example:
C++
#include <iostream> using namespace std; using ll = long long; // Here we alias "ll" to stand for long long and save typing it out int main() { ll a = 5; cout << a; return 0; }
4. «using for directives:
Using can be used as a directive to the compiler to tell it to include a particular header file. This is useful if you want to make sure that a particular header file is always included when you compile your code.
C++
#include <iostream> using std::cout; using std::endl; int main() { cout << "Hello, world!" << endl; return 0; }
Forms of «using» Keyword
The «using» keyword has two different forms:
- using namespace std; — This form will bring everything in the std namespace into your code.
- using std::cout; or using std:: cin; — This form will only bring the cout object into your code.
- using std:: endl;— This form is used to make only specific names in a namespace available in the current namespace.
- You can also use the «using» keyword with your own namespaces.
Note: if you have a namespace called «mynamespace», you could use it like this:
using namespace mynamespace;
Or, if you only wanted to bring in the «myclass» object from that namespace, you could use this form:
using mynamespace::myclass;
Limitation of «using» Keyword:
The «using» keyword can be very useful, but it can also lead to problems if you’re not careful.
- you need to be careful about using it with large codebases. The reason for this is that it can sometimes lead to name collisions.
- If you have multiple namespaces in a file, you have to fully qualify the names of any type in a namespace other than the one you are currently in.
- You cannot use the using keyword to make types in the global namespace visible.
- You cannot use the using keyword to make types in the System namespace visible.
- You cannot use the using keyword to make types in a parent namespace visible.
- You cannot use the using keyword to make static classes visible.
Note:
Imagine you have a codebase that includes both the «std» and «mynamespace» namespaces. If you use the «using» keyword to bring everything in both namespaces into your code, you might end up with two objects with the same name. This can cause compile-time errors or, worse, unexpected behavior at runtime.
To avoid these problems, it’s best to be explicit about which namespaces you’re using.
Example: if you only want to use the «std» namespace, you should write your code like this: using namespace std;
And if you only want to use the «mynamespace» namespace, you should write your code like this: using namespace mynamespace; This may seem like a lot of extra work, but it’s worth it to avoid problems down the road. The «using» keyword is a powerful tool that allows you to specify the use of a particular entity in your program. In particular, it is often used to specify the use of a namespace, class, or function.
Example 1:
C++
// C++ Program to implement // "using" keyword #include <iostream> using namespace std; int main() { cout << "Hello, world!" << endl; return 0; }
In this code, the «using» keyword is used to specify the use of the «cout» object from the «std» namespace. Without the «using» keyword, the code would not compile.
The «using» keyword can also be used to specify the use of a particular function or class.
Example 2:
C++
// C++ Program to implement // "using" keyword #include <iostream> #include <string> using std::cout; using std::endl; using std::string; int main() { string s = "Hello, world!"; cout << s << endl; return 0; }
In this code, the «using» keyword is used to specify the use of the «string» and «cout» objects from the «std» namespace. Without the «using» keyword, the code would not compile.
В данной статье будет рассказано о том, что собой представляет пространство имен в C#. Предстоит познакомиться с директивами типа using, а также рассмотреть наглядные примеры использования упомянутых компонентов.
Директива – это…
Директива – это указание. Определение соответствующего понятия в программировании напоминает термин «команда». Он используется для описания некоторых конструкций ЯП. Указывает, как компилятор или любой иной транслятор должен обрабатывать вводимые данные.
Директивы не являются частью грамматики. Они могут меняться в зависимости от компилятора. Обрабатываются процессором для определения поведения компилятора, а также функционируют в виде своеобразной функции внутренней параметризации.
Иногда подобные компоненты отвечают за определение глобального поведения, а в некоторых ситуациях действуют локально. В программах на C не являются обязательными. Они могут быть проигнорированы компилятором. Чаще всего носят предписывающий характер. В самом языке директивы (using и не только) не отвечают за выполнение тех или иных действий ЯП – они лишь корректируют поведение компиляторов.
Пространства имен – что это
Пространства имен (namespace) – это то, что позволяет выделить определенные части приложений в логические блоки. Поддерживается работа с:
- классами;
- интерфейсами;
- прочими элементами программного кода.
Подобная концепция позволяет сделать исходный код более читабельным и понятным. Пространства имен применяются специально для организации классов. Помогают контролировать область применения методов в более крупных проектах.
При помощи упомянутого элемента можно сохранить один набор имен (пример – имена классов) отличным от других наборов имен. Преимуществом такого подхода является то, что имена классов, объявленные в пределах одного пространства, не конфликтуют с аналогичными, но в других пространствах.
Членами могут выступать:
- пространства имен;
- структуры;
- делегаты.
Пространства имен можно охарактеризовать как группу классов, обладающих общими признаками.
Свойства
Перед тем как изучить директиву using, требуется запомнить следующие свойства именных пространств:
- организация масштабных проектов по созданию программных кодов;
- разделение происходит при помощи оператора «.» (точки);
- global – корневая область имен.
Using – директива, которая позволяет исключить требование на указание названия области имен для каждого класса. Данный компонент избавляет от полной классификации «названий». Далее соответствующий элемент будет рассмотрен более подробно.
Using – описание
Директивы using – это то, что производит импорт именных областей, избавляя разработчика от полной классификации имен стереотипов. Дает возможность задействовать определенные типы без указания полной области «названий» соответствующего типа. В базовой форме used используется импорт всех типов именных пространств.
Using directive поддерживает применение двух модификаторов:
- Модификатор global. Он будет работать точно так, как и добавление одной директивы use к каждому исходному документу в пределах проекта. Первое появление состоялось в C Sharp 10 версии.
- Модификатор static. Он производит импорт элементов static, а также вложенных типов из одного типа, а не из всех в пределах области «названий».
Оба модификатора можно использовать вместе для импорта статистических членов из того или иного типа во всех исходных проектных файлах.
Сейчас C# поддерживает создание псевдонимов для namespace. Для этого используется директива псевдонимов using:
using Project = PC.MyCompany.Project;
Модификатор global используется в using alias. Без него область будет ограничиваться файлом, в пределах которого находится директива.
Особенности
Директива using в C# имеет ряд особенностей. Она отображается в нескольких местах – все зависит от ситуации:
- Начало документа в исходном коде. Перед тем, как объявлять именные «области», а также типы.
- В любом namespace, но до пространственных имен, объявленных в первой «области». Здесь обязательно, чтобы не использовался модификатор global. Если он присутствует, директива using располагается перед всеми объявлениями.
Если не соблюдать данные принципы, при попытке обработки кода появится ошибка компиляции CS1529.
Директива using создается для того, чтобы использовать типы в пространстве имен без необходимости указания его самого. Если использовать using System, больше не придется вводить полный путь. Методы в приложении получится вызывать быстрее и короче.
Using – это ключевое слово, которое в C# имеет широкое применение. Оно используется для создания операторов using. Данные элементы используются для грамотной обработки объектов IDisposable – шрифтов и файлов.
Псевдонимы
Using alias – это директива, позволяющая создавать псевдонимы для namespaces. У нее не может быть открытого универсального типа в правой части. Создать его для List<T> не получится, но можно для List<int>.
Если отсутствует импорт пространств имен System, полные имена базовых типов окажутся недоступными:
Для подключения using alias потребуется:
- Добавить в References желаемые сборки.
- Открыть соответствующую папку и кликнуть по сборке правой кнопкой мышки.
- Выбрать раздел Properties.
- Нажать на свойство Aliases.
После этого остается заменить значение global на свое собственное название.
Наглядный пример
Для того, чтобы хорошо разбираться в директивах и их uses, стоит изучить наглядный пример. Ниже приведен фрагмент кода. Он будет взять за «базу» для изучения directive и оператора:
Здесь в операторе using в круглых скобках написано выражение, обработка которого приводит к получению специального объекта. Он будет реализовывать интерфейс System IDisposable. Если объект не реализует интерфейс, на экране появится сообщение об ошибке.
В приведенном фрагменте объект определяется за пределами оператора using. Соответствующий код может быть записан так:
После обработки блока using в C происходит автоматический вызов метода Dispose(). Он осуществляется для переданного ранее объекта и располагается в интерфейсе System.IDisposable.
При использовании потоков необходимо запомнить – при завершении оперирования ими требуется завершать соответствующие процессы. Аналогичные принципы применяются к базам данных, сокетам, иным источникам. Метод Dispose() будет каждый раз завершать работу с потоком и высвобождать ресурсы.
А вот более простой фрагмент программы:
Здесь:
- Using namespace std указывает компилятору на импорт всех имен из пространства std в текущую область видимости. В приведенном примере таковым является main().
- После использования неполного идентификатора count происходит преобразование в std::count.
Данный вариант подойдет для того, чтобы упрощать программный код. С ним не придется вручную прописывать и обновлять все неполные имена до полных.
Как лучше освоить
C# – язык, который пользуется спросом у разработчиков. Для того, чтобы хорошенько изучить его директивы, а также пространства имен и иные элементы, можно заняться самообразованием. Это не лучшее решение – обучение может затянуться, а доказать полученные навыки и знания документально окажется невозможно. Именно поэтому рекомендуется отдать предпочтение дистанционным онлайн курсам.
Они рассчитаны на широкую публику и прекрасно совмещаются со всеми делами – уроки можно просматривать в любое время. Курсы рассчитаны на срок до 12 месяцев. Пользователи получают шанс выбрать одновременно одно или несколько инновационных IT-направлений для изучения. Процесс сопровождается домашними заданиями и богатой практикой. В конце обучения выдается электронный сертификат, подтверждающий приобретенный спектр навыков и знаний.
Хотите освоить современную IT-специальность? Огромный выбор курсов по востребованным IT-направлениям есть в Otus!
С каждым днем программирование становится все более сложным и гибким. В языке C создаются и разрабатываются концепции, которые помогают упростить работу с ресурсами и улучшить читаемость кода. Одной из таких концепций является механизм управления ресурсами, позволяющий в автоматическом режиме освобождать их. Этот подход дает разработчикам возможность сосредоточиться на функционале, отодвигая в сторону беспокойства о корректном завершении работы с ресурсами.
Когда дело доходит до работы с ресурсами, правильная организация кода становится особенно важной. Ключевая роль в этом процессе отведена имени using, который позволяет заменить целые блоки с clean-up кодом на более лаконичные конструкции. Такая практика также способствует созданию более надежных и читаемых программ, минимизируя вероятность появления ошибок, связанных с неправильным управлением ресурсами.
Имя using могут внедрять в свои программы как начинающие, так и опытные программисты. Например, ниже показано, как с помощью данного механизма можно упростить управление файловыми потоками:
#include <stdio.h> void readFile() { FILE *file; file = fopen(example.txt, r); if (file == NULL) { printf(Ошибка открытия файла!); return; } // Процесс чтения данных fclose(file); }
Пример выше демонстрирует стандартный подход к работе с файлами в языке C. Немного модифицировав его с использованием идеи RAII (Resource Acquisition Is Initialization), программист может значительно улучшить читаемость и надежность своего кода. Функционал управления ресурсами, встроенный в языке через ключевое слово, способствует не только автоматическому освобождению ресурсов, но и минимизации человеческого фактора в процессе их использования.
Основы оператора using в C
В языках программирования определенные элементы синтаксиса помогают сделать код более чистым и управляемым. Некоторые ключевые слова позволяют автоматизировать управление ресурсами и их освобождение. В C нет прямого аналога оператора using, но концепция управления жизненным циклом переменных применяется через другие техники.
Одним из важных элементов управления ресурсами в C является RAII (Resource Acquisition Is Initialization). Этот метод подразумевает, что выделение ресурса совмещается с инициализацией объекта, а освобождение происходит при завершении его времени жизни. Например, при работе с указателями на память рекомендуется использовать malloc для выделения и free для освобождения ресурсов. Этот подход позволяет контролировать жизненный цикл данных и избегать утечек.
Для автоматизации подобных процессов можно использовать функции и макросы, которые позволяют сократить количество повторяющихся statements. Например, вы можете создать функцию-оболочку, которая автоматически высвобождает память по завершении работы с данными:
void process_data() { char* buffer = malloc(100); if (buffer == NULL) { // Обработка ошибки выделения памяти return; } // Работа с данными free(buffer); // Явное освобождение памяти }
При разработке сложных приложений правильно организованное управление памятью может значительно увеличить надежность системы. Даже несмотря на отсутствие конкретного ключевого слова, похожего на using, философия грамотного обращения с памятью в языке C требует дисциплины и тщательного подхода к структуре кода.
Таким образом, понимание концепции управления ресурсами в C позволяет эффективно организовывать код, используя лучшие практики и доступные конструкции языка для снижения накладных расходов и лучшего контроля за очисткой. Это основывается на следовании принципам структурного программирования и RAII, упрощая работу с памятью и ресурсами.
Историческая справка об операторе using
Первоначально concept using появилась в языке программирования C++ как средство упрощения доступа к именам, в частности для сокращения пространства имен. Эта конструкция стала ответом на сложность работы с большими объемами кода, где часто встречалось дублирование имен и необходимость сложной навигации в больших библиотеках.
В начале 1990-х годов, когда C++ только набирал популярность, развитие языка было обусловлено стремлением решить проблемы, с которыми сталкивались программисты в C. Среди этих задач были создание более управляемого кода, улучшение читаемости, структуризация библиотек и модулей. Как результат, был введен механизм управления событиями и ресурсами, где statements и их использование играли ключевую роль.
Кроме C++, тренд введения подобных конструкций продолжился и в других языках. Например, в C# введение такой конструкции также предоставило мощные средства для контроля ресурсов с применением краткости и ясности.
// Пример использования в C++ #include <iostream> using namespace std; int main() { cout << Hello, World!; return 0; }
Как видно из примера, благодаря using statements we can избежать повторного написания префикса пространства имен. Подобная практика экономит время и делает код более читаемым для других разработчиков.
Синтаксис и структура использования
В языке программирования C концепция управления ресурсами и пространства имен наилучшим образом осуществляется через использование ключевых понятий и структурных элементов кода. В данном разделе рассматривается то, как можно достичь понятного и структурированного кода с использованием соответствующих конструкций.
Пространства имен помогают избежать конфликтов и упрощают читаемость кода. Хотя в C непосредственно не существует оператора using, роль управления элементами пространства имен переходит к директивам препроцессора, таким как #include. Структура использования директивы основана на логике добавления содержимого внешних файлов в исходный код.
#include <stdio.h>
Слог include позволяет компилятору пристыковать ресурс, имя которого указано в угловых скобках. Здесь ключевое слово include выступает в роли средства интеграции ресурса в текущий модуль программы.
Структура обращения к именам также включает использование #define для определения макросов, предоставляя программисту возможность определять константы для облегчения чтения и улучшения поддержки кода:
#define PI 3.14159
Данная конструкция позволяет применять константное значение в любом месте программы через определенное имя, обеспечивая простоту и ясность. При правильном создании сельфий макросов, вы можете добиться повышения читабельности и упрощения структуры самого кода.
В завершение создания структурных единиц программы, набор указанных директив и ключевых концепций позволяет улучшить баланс между гибкостью и организованностью, поддерживая контур программного обеспечения чистым и логически последовательным.
Основные случаи применения оператора
Когда дело касается повышения читаемости и упрощения кода, данный оператор может быть крайне полезным инструментом в арсенале каждого программиста. Он облегчает работу с пространствами имен и помогает избежать конфликтов или путаницы в коде.
Основные ситуации, когда его можно применять:
- Упрощение доступа к элементам пространств имен: когда в коде используются элементы из различных библиотек, этот оператор может помочь сделать доступ к ним более приятным и понятным.
- Избежание конфликтов имен: если в проекте встречаются одинаковые имена, его помощь станет ключевой для разрешения таких ситуаций.
- Повышение читаемости кода: позволяет сократить количество символов в коде и сделать его более понятным для других разработчиков.
Например, вместо того, чтобы каждый раз указывать полное пространство имен, можно указать его разово, упростив код:
// Вместо: std::vector numbers; // Используем: using std::vector; vector numbers;
Правильное использование помогает избежать разночтений и улучшает структурированность кода, особенно в крупных проектах, где пространство имен может быть сложным и обширным.
Особенности работы с пространство имен
Пространства имен в языке C не так явно выражены, как в некоторых других языках программирования, таких как C++ или C#. Вместо этого, управление именами происходит через эффективную аналогию использования файлов и функций. Это означает, что структурирование и раздельное компилирование кода должно быть осуществлено так, чтобы избежать переполнения одинаковыми именами. Стратегии включают организацию кода в библиотеки или использование уникальных префиксов для имен переменных и функций.
Когда мы говорим об использовании пространства имен, необходимо учитывать не только их назначение, но и то, как они взаимодействуют с другими элементами программы. Ключевым аспектом в этой задаче является знание того, какие имена уже задействованы в проекте, а также понимание, какие из них могут пересекаться с именами в стандартной библиотеке языка C. Это поможет избежать неожиданных конфликтов имени.
Рассмотрим простой пример разделения кода с помощью пространства имен для избежания конфликтов:
// Пример 1: Использование уникальных префиксов void math_calculate(int a, int b); int math_add(int a, int b);
В данном примере добавление префикса ‘math_’ к функциям помогает избежать конфликтов с потенциально похожими именами в других частях программы. Такой подход делает ваш код более понятным и структурированным.
Взаимодействие с пространства имен также подразумевает способность управлять областью видимости и доступом к именам в проекте. Использование ключевых слов, таких как static, позволяет ограничить видимость имени функции или переменной только текущим файлом, тем самым предотвращая возможные конфликты с аналогичными именами в других файлах модуля.
// Пример 2: Ограничение видимости static int utility_function(int data);
Правильная стратегия использования пространства имен и понимание того, какие имена доступны в вашем проекте, создаст основу для поддерживаемого и чистого кода. Это одна из основополагающих дисциплин в более крупной концепции модульности и повторного использования кода.
Сравнение с аналогами в других языках
Когда мы рассматриваем способы управления зависимостями и областями видимости в разных языках программирования, становится ясно, что различные системы предлагают уникальные подходы для организации кода. Рассмотрим, как схожие механизмы работают во многих популярных языках программирования и что можно узнать из их способов структурирования кода и управления пространствами имен.
В языке C++, ключевое слово using активно применяется для удобного доступа к членам пространств имен. Это позволяет избегать избыточных конструкций, упрощая доступ к объектам. В свою очередь, в Java отсутствует аналогичный механизм, но подобную функциональность обеспечивает import, позволяя подключать пакеты или конкретные классы в текущий экземпляр программы.
В языке C# оператор using имеет две разные цели: импорт пространств имен и управление ресурсами в блоках using. Это снижает вероятность утечки ресурсов, когда вовремя не освобождаются используемые объекты, такие как файлы или сетевые соединения. В отличие от C#, в Python схожий результат достигается с помощью конструкции with, что делает код более читаемым и безопасным.
Рассмотрение возможностей JavaScript показывает, что import используется для управления модулями, что схоже с концепцией изгораживания кода в C и его аналогах. Потребность в компактности и переиспользовании кода привела к разработке подходов, которые могут различаться стилистически, но по своей сути преследуют одну и ту же цель.
Подведем итоги:
| Язык | Ключевое слово | Назначение |
|---|---|---|
| C++ | using | Упрощение доступа к пространствам имен |
| Java | import | Подключение пакетов и классов |
| C# | using | Импорт и управление ресурсами |
| Python | with | Управление контекстом выполнения |
| JavaScript | import | Организация модулей |
Изучая различные способы применения подобных конструкций, программисты могут выбирать более подходящие инструменты для более эффективного решения задач в зависимости от специфики используемого языка.
Практические примеры и советы
Пример первый. Для установки связи между пространством имён и использованием элементов этого пространства можно написать следующий код:
using namespace std; int main() { cout << Введите ваше имя: ; string имя; cin >> имя; cout << Привет, << имя << ! << endl; return 0; }
Совет: в крупных проектах лучше избегать ширококруглого применения, чтобы избежать неоднозначности с именами. Вместо этого используйте конкретные элементы:
#include <iostream> int main() { std::cout << Введите ваше имя: ; std::string имя; std::cin >> имя; std::cout << Привет, << имя << ! << std::endl; return 0; }
Когда вы имеете дело с ресурсами, важно учесть их освобождение. В C++ using часто используется с конструкциями, которые освобождают ресурсы автоматически. Это называется RAII (Resource Acquisition Is Initialization). Например, при работе с файлами:
#include <fstream> void readData() { using std::ifstream; ifstream file(data.txt); // Использование данных из файла }
Совет: всегда учитывайте необходимость выразительного синтаксиса при работе с пространствами имён. В местах, где одно и то же имя встречается в нескольких пространствах, предпочитайте полный путь. Это делает ваш код более явным и защищает его от потенциальных ошибок.
Подводя итог, применение using упрощает код и улучшает его восприятие, особенно в командах разработчиков. Применяйте советы грамотно, учитывая уникальные особенности каждого проекта.
Комментарии
When I write code I don’t only want to write code that is correct. I also want to write code that is understandable, and maintainable. I want to deliver code that is easy to read not only for the compiler but also for other human beings. After all, humans will read my code more frequently than compilers.
I have been thinking what are the single most important keywords that help us write readable code. Probably this question doesn’t make much sense, but const and using are definitely among these. We already discussed const a lot, this time it’s time to see how using using can improve our code.
We are going to review the 4 ways we can use it:
- type aliasing with
using - introducing complete namespaces with
using-directive - introducing members of another namespace with
using-declaration - importing class members with
using-declaration
Aliasing
In old C++ we could use typedef to give another name, to give an alias for our types. Sometimes you might want to use it instead of strong typing, just to benefit from more meaningful names like int.
1 |
typedef int Horsepower; |
Other times you want to shorten long types for easier usage:
1 |
typedef std::vector<std::string>::iterator Iterator; |
Since C++11 we can use using instead of typedef to achieve the same results.
1 2 |
using Horsepower = int; using Iterator = std::vector<std::string>::iterator; |
Why would you use using over the good old typedef? Just read the above statements! Exactly like the T.43 core guideline says, it’s more readable! The keyword has a very clear meaining, then the name comes first and the old comes after a =.
Besides, using can be used more generally. It can be used for template aliases where typedef would lead to a compilation error.
1 2 3 4 5 |
template<typename T> typedef std::map<int, T> MapT; // error template<typename T> using MapT = std::map<int, T>; // OK |
Using-directive in namespace and block scope
You’ve probably seen many code examples that right after the #include statements contain the line using namespace std.
You’ve probably seen lots of such application code.
You’ve probably been told that it’s bad.
It’s particularly bad if you do in at the global scope in a header file, just like [SF.7 from the Core Guidelines says]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// bad.h #include <iostream> using namespace std; // bad // user.cpp #include "bad.h" // some function that happens to be named copy bool copy(/*... some parameters ...*/); int main() { // now overloads local ::copy and std::copy, could be ambiguous copy(/*...*/); } |
In my opinion, even the fact that as a reader you cannot be sure where a function comes from is bad. This is a simplistic example, but when you use using namespace in a long .cpp file it’s hard to keep track of where certain objects come from. I prefer having using-declarations instead and I also often introduce alias namespaces.
1 2 3 4 5 6 7 8 9 10 11 12 |
//some.h #include <other.h> using mcs = mynamespace::component::subcomponent; msc::Class foo(); //some.cpp msc::Class foo() { using msc::AnotherClass; AnotherClass bar; // ... } |
As such, I don’t pollute the global namespace. What you have to keep in mind is that when you introduce a using-directive into a header file at the global namespace header, you don’t just mess things up in the current scope.
If you include the header file in other files, you’ll also bring the inclusion of all those introduced symbols. If you introduce different header files with different global levels using-directives, the situation becomes even worse and the results of name lookup might depend on the order of inclusion.
To avoid all such problems, just follow SF.7 and don’t write using namespace at global scope in a header file.
Using-declaration in namespace and block scope
While the using-directive brings all the symbols of a namespace into the current scope, a using-declaration will bring only one item!
1 2 |
using std::string; string foo{"bar"}; |
In the above example, we just demonstrated how it works. After using std::string, we can refer to std::string without mentioning the std namespace.
It’s still something not to overuse! A using-declaration may also expand an overload set. It’s less dangerous to use it at a file scope than having a using-directive at the same scope, but risks still remain.
Starting from C++20, you can also introduce scoped enumerators into a namespace of block scope!
1 2 3 4 5 6 |
enum class Color { red, green, blue }; class MyClass { using Color::red; Color c = red; // This is OK from C++20 }; |
In fact, it would also work with the old-style unscoped enum, but why would we do that?
Importing base class members with using-declaration
With using-declaration, you can introduce base class members — including constructors — into derived classes. It’s an easy way of exposing protected base class members as public in the derived class. It can be used both for functions and variables.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> class Base { protected: void foo() { std::cout << "Base::foo()\n"; } int m_i = 42; }; class Derived : public Base { public: using Base::foo; using Base::m_i; }; int main() { Derived d; d.foo(); std::cout << d.m_i << '\n'; } /* Base::foo() 42 */ |
If you try to modify the above example and remove any of the two using-declarations, you’ll see the compilation failing.
If the derived class already has a member with the same name, the compilation will not. The imported symbol from the base class will be hidden.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <iostream> class Base { protected: void foo() { std::cout << "Base::foo()\n"; } }; class Derived : public Base { public: using Base::foo; void foo() { std::cout << "Derived::foo()\n"; } }; int main() { Derived d; d.foo(); } /* Derived::foo() */ |
I find this technique really useful for unit testing. When you’re writing a mock by hand, you often have to expose protected member functions from the base class, from the class that you are about to mock.
One way of doing it is forwarding the call.
Hopefully, the function’s name in the mock is not changed, but I’ve seen it a couple of times. It really puts an extra burden on the maintainers when they realize that there is a better option.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class ClassUnderTest { public: virtual void testMe() { } virtual void testMeToo() { } }; class MockClassUnderTest : public ClassUnderTest { public: void testMe() override { ClassUnderTest::testMe(); } void mockedTestMeToo() { ClassUnderTest::testMeToo(); } }; |
Apart from tying a lot of unnecessary code, the problem above is that if the parameter list of testMe or testMeToo changes, you’ll also have to update MockClassUnderTest. You can get rid of that need by using using.
1 2 3 4 5 |
class MockClassUnderTest : public ClassUnderTest { public: using ClassUnderTest::testMe; using ClassUnderTest::testMeToo; }; |
Now we have less code and it’s more understandable what’s happening. As a bonus, even the maintenance is simplified.
Conclusion
In this article, we discussed the 4 different ways that we can use the using keyword. It’s the right way to create aliases and import base class members in derived classes. At the same time, they can be also used to introduce whole namespaces into the current scope which can be particularly dangerous. Last but not least, using can also introduce single types to the current scope which is a less dangerous option than introducing whole namespaces, still, it should be used with care.
Connect deeper
If you liked this article, please
- hit on the like button,
- subscribe to my newsletter
- and let’s connect on Twitter!
C# и .NET обеспечивают управление ресурсами для управляемых объектов через сборщик мусора. Вам не нужно явно выделять и освобождать память для управляемых объектов. Операции очистки для любых неуправляемых ресурсов должны выполняться в деструкторе C#.
Чтобы позволить программисту явно выполнять эти действия по очистке, объекты могут предоставлять метод Dispose, который можно вызывать, когда объект больше не нужен. Оператор C# — using определяет границу объекта, за пределами которой объект автоматически уничтожается. Оператор using в C# завершается, когда заканчивается блок операторов using или когда выполнение выходит из блока операторов using косвенно, например, возникает исключение.
Оператор using позволяет указать несколько ресурсов в одном операторе. Объект также может быть создан вне оператора using. Объекты, указанные в блоке using, должны реализовывать интерфейс IDisposable. Платформа вызывает метод Dispose объектов, указанных в операторе using, при выходе из блока.
Класс MyManagedClass не является частью .NET Framework и используется в примере только для демонстрации. Класс MyManagedClass должен быть освобожден явно и реализует интерфейс IDisposable.
using (MyManagedClass mnObj = new MyManagedClass())
{
......
mnObj.Use(); //используется объект mnObj
......
} //компилятор уничтожит объект mnObj здесь
......
//Пример 2
......
MyManagedClass mnObj =new MyManagedClass()
using (mnObj)
{
......
mnObj.Use();//используется объект mnObj
......
}//объект mnObj будет уничтожен здесь
......
Обратите внимание, что директива using в C# отличается от оператора using. Директива «using» используется для предоставления псевдонима для пространства имен.
// псевдоним пространства имен
using System;
using System.IO;
class Program {
static void Main(string[] args)
{
// путь к файлу
string file = @"M:\Documents\Textfile.txt";
// Запись в файл с помощью StreamWriter
string[] textLines2 = { "Myrusakov.ru",
"Myrusakov.ru" };
// объект writer будет существовать в
// в пределах конструкции using
using(StreamWriter writer = new StreamWriter(file))
{
foreach(string ln in textLines2)
{
writer.WriteLine(ln);
}
}
// вывод на экран содержимого файла
Console.WriteLine(File.ReadAllText(file));
Console.ReadKey();
}
}
Таким образом, открывая файл в C# следует позаботиться об использовании ресурсов памяти, для чего и используется конструкция using.
-
Создано 12.04.2022 13:16:30
-
Михаил Русаков
Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!
Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.
Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
-
Кнопка:
Она выглядит вот так:
-
Текстовая ссылка:
Она выглядит вот так: Как создать свой сайт
- BB-код ссылки для форумов (например, можете поставить её в подписи):
