Qbik-club
Дата публикации:15.11.21 17:03;Автор:Евгений;Категория: программирование;Теги:, , ;

Подключение библиотек и пространство имён

В данной публикации я предлагаю продолжить тему изучения языка программирования С++. На сегодняшний день мы уже довольно глубоко продвинулись в изучении этого языка, уже знаем о тепеменных, функциях, условных операторах, преобразовании типов...

И сегодня мы познакомимся с последними «магическими строчками», до которых до сих пор не дошли. Это подключение библиотек и пространство имён. И как всегда, не пугайтесь страшных слов, сейчас всё разложим по полочкам! ;)

Подключение библиотек и пространство имён

Что такое библиотека

И так, давайте разберёмся, что же такое библиотека и как она работает. Давайте, перед тем, как переходить к сложному, к рассказу о STL, расскажем всё на простом примере. представьте себе, что вы являетесь разработчиком в компании, которая занимается рекламой. У вас есть целая сеть уличных табло, выводящих текст на экран. Ваша задача — получить из базы текст и отправить его в функцию, которая выводит текст на экран. Задача — довольно простая.

#include <iostream>
#include <string>

using namespace std;

string GetText(){
    return "Строка для вывода";
}

void PrintText(string text){ // имитируем работу функции, выводящей текст на экран
    cout << "-> " << text << ";" << endl;
}

int main(){
    setlocale(LC_ALL, "ru_RU.UTF-8");

    string MyText = GetText(); // Получаем строку из БД

    PrintText(MyText); // Выводим её на наш воображаемый монитор

    return 0;
}

Как не сложно догадаться, вывод программы выглядит так:

Пример работы программы

Казалось бы, всё красиво и нет никаких проблем. Но представьте себе, что нам в компанию прислали новые мониторы, от другой фирмы. И нам теперь надо писать новую функцию PrintText(). А если таких компаний 5? 10? И под все нужно писать новые функции? Давайте не забывать о том, что в реальности эти функции вывода были бы гораздо объёмное, чем одна строка. И не проще ли попросить самих разработчиков дать нам готовую функцию, которой достаточно передать строку для отображения?

Именно случай, когда нам присылают готовый набор функций, готовых для выполнения конкретной задачи, и называется библиотекой. По этому на практике наш код выглядел бы вот так:

#include <string>
#include "print.h"

using namespace std;

string GetText(){
    return "Строка для вывода";
}

int main(){
    setlocale(LC_ALL, "ru_RU.UTF-8");

    string MyText = GetText(); // Получаем строку из БД

    PrintText(MyText); // Выводим её на наш воображаемый монитор

    return 0;
}

Тут нас интересуют первые две строки. Первая — это подключение библиотеки string. Вторая — print.h, которая и добаляет в нашу программу функции для работы нашими дисплеями. Но почему в одном случае название «обёрнуто» фигурными скобками, а во втором — кавычки?

Тут есть тонкость обработки путей. Дело в том, что когда мы пишем include — мы как бы говорим компилятору, что подставь на место вот этой строки — содержимое файла, который находится по такому вот адресу. Как можно почитать на сайте microsoft, разница заключается в следующем:

В кавычках

Препроцессор ищет включаемые файлы в следующем порядке:

  1. В том же каталоге, что и файл, содержащий #include инструкцию.
  2. В каталогах открытых в данный момент файлов включения в порядке, в котором они были открыты. Поиск начинается в каталоге родительского включаемого файла, а затем выполняется в каталогах всех включаемых файлов-прародителей.
  3. Вдоль пути, указанного в каждом /I параметре компилятора.
  4. Вдоль путей, указанных в INCLUDE переменной среды.
С угловыми скобками

Препроцессор ищет включаемые файлы в следующем порядке:

  1. Вдоль пути, указанного в каждом /I параметре компилятора.
  2. При компиляции происходит в командной строке вместе с путями, заданными INCLUDE переменной среды.

Но если сказать проще, то для себя я использую следующее правило. Стандартные библиотеки из разряда STL (о них поговорим ниже) я подключаю через угловые скобки, а те, что или написал сам, или скачал из интернета — через кавычки. Всё просто и понятно! :)

Теперь давайте попробуем самостоятельно что то написать, используя подключаемые файлы. А за одно узнаем о то, что такое перегружаемые функции, которые мы, если я не ошибаюсь, ещё не использовали. Давайте представим себе, что у нас из БД можно получить как номер монитора, так и его текст. Конечно, можно отдельно написать функцию для получения номера монитора, отдельно - для текста. Отдельно вывод номера монитора, отдельно текста. На практике, конечно, так и есть, но ведь почему бы это всё не «спрятать» в библиотеке?

Для этого давайте создадим файл get.cpp со следующим содержимым:

using namespace std;

void getBd(int &id){
    id = 19;
}

void getBd(string &text){
    text = "Текст, отображаемый на экране";
}

И файл print.cpp со следующим содержимым:

using namespace std;

void Print(string text){
    cout << "Текст: " << text << ";" << endl;
}

void Print(int ID){
    cout << "ID монитора: " << ID << ";" << endl;
}

Как видим, мы создали две перегруженные функции, которые получают номер монитора или его текст в зависимости от того, какие параметры приняли. Но как видим, в данных файлах нет функции main(). Именно это и отличает файлы — библиотеки от нашего основного файла. Ведь их задача — лишь добавить в нашу программу новые фунции, но не более того. Давайте теперь посмотрим на наш основной файл main.cpp, он выглядит так:

#include <string>
#include <iostream>
#include "get.cpp"
#include "print.cpp"

using namespace std;

int main(){
    setlocale(LC_ALL, "ru_RU.UTF-8");

    int ID;
    string TextMonitor;

    getBd(ID);
    getBd(TextMonitor);

    Print(ID);
    Print(TextMonitor);


    return 0;
}

Как видим, сразу мы подключаем библиотеку ввода вывода, затем строки, а затем и наши только что написанные файлы. Но почему именно в таком порядке? Почему сразу стандартные библиотеки, а затем наши? Всё дело в том, что, как я говорил ранее, на этапе компиляции эти строки превращаются в то, что написано в файлах. И если бы мы подключили файл с функцией Print() раньше, чем подключили библиотеку iostream — мы бы получили ошибку т.к. функция не может воспользоваться объектом cout, который ещё не подключен.

Давайте всё же посмотрим, что выведет наша программа:

Пример работы тестовой программы

Что такое стандартная библиотека

Я уже не раз упоминал о стандартной библиотеке STL. Но что это такое? Википедия Ивановна описывает это так:

В языке программирования C++ термин Стандартная Библиотека означает коллекцию классов и функций, написанных на базовом языке. Стандартная Библиотека поддерживает несколько основных контейнеров, функций для работы с этими контейнерами, объектов-функции, основных типов строк и потоков (включая интерактивный и файловый ввод-вывод), поддержку некоторых языковых особенностей, и часто используемые функции для выполнения таких задач, как, например, нахождение квадратного корня числа. Стандартная Библиотека языка C++ также включает в себя спецификации стандарта ISO C90 стандартной библиотеки языка Си. Функциональные особенности Стандартной Библиотеки объявляются внутри пространства имен std.

Но если сказать проще, то есть целый набор решений, которые нужны практически каждому. К примеру практически в каждой программе нам нужно выводить текст в терминал (консоль). Разумеется, писать самому функцию (объект) для этих целей — не самое разумное решение. По этому данный код был однажды написан, принят, как стандарт и теперь мы его можем подключать как стандартное решение для вывода текста на экран.

Или к примеру в стандартном языке С++ — нет строк. Опять же, данная проблема была решена через класс string, который мы можем подключить из стандартной библиотеки функций и не заморачиваться с самостоятельным решением проблемы.

Это точно такие же файлы, которые вы можете написать самостоятельно, но их просто заранее подготовил кто то другой! ;)

К примеру, если у вас Linux, то все данные файлы вы можете найти по этому пути: /usr/include, для windows путь будет другой, но я не стал заморачиваться и гуглить. Если нашли — расскажите об этом в комментариях! :)

Что такое пространство имён

И так, сегодня мы уже узнали о том, что такое библиотеки, зачем они нужны, что делают и чем отличается библиотека STL от того, что мы пишем сами или скачиваем из интернета. Но что же такое пространство имён? Выше, из цитаты вы могли даже заметить упоминание пространства имён std, которое у нас всегда упоминалось в коде.

На самом деле, тут нет ничего сложного. Дело в том, программисты, создающие программы и библиотеки — это тоже люди, которым так же свойственно называть свои функции и переменные одинаковыми именами. И именно для того, чтоб избежать конфликта между двумя функциями, с одинаковыми именами в разных библиотеках и было введено пространство имён.

Давайте ещё раз вернёмся к нашей библиотеке с дисплеями. В этот раз файл с получением данных мы оставим без изменений:

using namespace std;

void getBd(int &id){
    id = 19;
}

void getBd(string &text){
    text = "Текст, отображаемый на экране";
}

А вот текст в файле print.cpp заменим. Как видим, тут у нас исчезает using namespace std. И вместо этого — две фигурные скобки, которые ограничивают две области видимости для двух пространств имён.

namespace asus{
    void Print(string text){
        cout << "Asus <- " << text << ";" << endl;
    }

    void Print(int ID){
        cout << "Asus ID: " << ID << ";" << endl;
    }
}

namespace samsung{
    void Print(string text){
        cout << "Samsung <- " << text << ";" << endl;
    }

    void Print(int ID){
        cout << "Samsung ID: " << ID << ";" << endl;
    }
}

И в конце, давайте же наконец посмотрим, как это выглядит для нашей программы:

#include <string>
#include <iostream>
#include "get.cpp"
#include "print.cpp"

int main(){
    setlocale(LC_ALL, "ru_RU.UTF-8");

    int ID;
    string TextMonitor;

    getBd(ID);
    getBd(TextMonitor);

    asus::Print(ID);
    asus::Print(TextMonitor);

    std::cout << "-----------------------" << std::endl;

    samsung::Print(ID);
    samsung::Print(TextMonitor);


    return 0;
}

Как видим, тут using namespace std так же исчезло, но и вызов функций так же изменился. Теперь, когда у нас нет namespace, компилятор не понимает, из какого пространства имён получать ту или иную функцию. И по этому нам нужно явно указывать для начала пространство имён, затем два двоеточия, а затем — имя функции.

Обратите внимание! Если у нас явно не указано using namespace std — так нужно поступать не только с функциями, лежащими за пределами std, но и с любыми другими. К примеру с хорошро нам знакомым cout. Чтоб этого избежать — можем использовать такой вариант:

#include <string>
#include <iostream>
#include "get.cpp"
#include "print.cpp"

using namespace std;

int main(){
    setlocale(LC_ALL, "ru_RU.UTF-8");

    int ID;
    string TextMonitor;

    getBd(ID);
    getBd(TextMonitor);

    asus::Print(ID);
    asus::Print(TextMonitor);

    cout << "-----------------------" << endl;

    samsung::Print(ID);
    samsung::Print(TextMonitor);


    return 0;
}

Как видим, теперь, чтоб обратиться к std — у нас нет необходимости обращаться к пространству имён, оно используется по умолчанию, которое указано в namespace. Но чтоб обратиться к функции из другого пространства имён — мы его указываем.

Пример работы программы с разным пространством имён

И в заключении

И в заключении давайте уже наведём окончательный марафет в нашей программе. А именно — избавимся от целого полотна include в верхней части. Для этого создадим файл header.h и запишем в его следующее:

#include <string>
#include <iostream>
#include "get.cpp"
#include "print.cpp"

А в main.cpp запишем следующее:


#include "header.h"

using namespace std;

int main(){
    setlocale(LC_ALL, "ru_RU.UTF-8");

    int ID;
    string TextMonitor;

    getBd(ID);
    getBd(TextMonitor);

    asus::Print(ID);
    asus::Print(TextMonitor);

    cout << "-----------------------" << endl;

    samsung::Print(ID);
    samsung::Print(TextMonitor);


    return 0;
}

И так, что мы тут сделали? На самом деле довольно очевидно. Мы «выбросили» все заголовочные файлы в один отдельный файл. Это освободило наше рабочее пространство от всего мусора, который визуально замусоревает пространство. На самом деле даже using namespace std; мы можем выбросить в этот заголовочный файл, чтоб не добавлять его каждый раз, но делать этого я крайне не рекомендую, дабы избежать целой кучи проблем в будущем.

И к слову говоря, на данную тему есть целая куча мнений. К примеру вот одно на хабре, которое я рекомендую почитать на последок.

А у меня на сегодня всё. В качестве домашнего задания, попробуйте немного модифицировать получившуюся библиотеку. Сделайте один основной монитор по умончанию, который в пространстве std, а два остальных — разделите на два брэнда. Но в качестве усложнения задачи — получение данных из БД — тоже разделите на брэнды, чтоб каждый монитор получал свои данные из своей области видимости.

Публикация относится к тематической подборке: «Уроки C++»

В данной подборке вы по шагам узнаете о увлекательном мире С++ и узнаете, на сколько глубока кроличья нора. Узнаете, для чего используется этот язык программирования, что с его помощью можно сделать и научитесь самостоятельно создавать самые сложные программы...

Понравилась публикация? Поделись ей с друзьями!

Понравился сайт? Подпишьсь на нас в соцсетях!

Мы в TelegramМы ВконтактеМы в ТвиттерМы на фейсбукМы в одноклассниках
Опубликовать
Загрузка рекомендуемых публикаций