Главная страница
Навигация по странице:

  • 4.3. Перегрузка шаблонов функций

  • 4.4. Специализации шаблонов

  • 4.6. Стандартная библиотека шаблонов Перечислим, что содержится в стандартной библиотеке шаблонов: Классы и шаблоны для организации потоков ввода/вывода

  • Локализация Поддержка различных языков:include include Поддержка языка программирования С++

  • Учебное пособие для студентов Авторы А. Н. Вальвачев, К. А. Сурков, Д. А. Сурков, Ю. М. Четырько Содержание Содержание 1


    Скачать 2.61 Mb.
    НазваниеУчебное пособие для студентов Авторы А. Н. Вальвачев, К. А. Сурков, Д. А. Сурков, Ю. М. Четырько Содержание Содержание 1
    Дата15.01.2023
    Размер2.61 Mb.
    Формат файлаdoc
    Имя файлаlab_OOTP.doc
    ТипУчебное пособие
    #886736
    страница33 из 33
    1   ...   25   26   27   28   29   30   31   32   33
    Глава 4

    4.1. Шаблоны

    Шаблоны обеспечивают непосредственную поддержку обобщенного программирования. Они представляют собой параметризованные классы и параметризованные имена функций.

    Шаблон определяется с помощью ключевого слова template:

    template

    class basic_string

    {

    public:

    basic_string();

    basic_string(const T*);

    basic_string(const basic_string&);

    private:

    T*str;

    };
    typedef basic_string string;

    typedef basic_string wstring;

    Вместо слова typename часто записывают слово class, но параметром шаблона может быть любой тип данных. С точки зрения компилятора, шаблон является макроподстановкой, поэтому шаблонные классы определяются целиком в заголовках файлов (в h-файле, а не в cpp-файле).

    Методы шаблона описываются следующим образом:

    template

    basic_string::basic_string(const *T)

    {

    ...

    }

    4.2. Шаблоны функций

    Допускается применение шаблонов с целью реализации абстрактных алгоритмов, то есть шаблонов функций.

    template

    void sort(vector& v);

    При вызове шаблонных функций компилятор подставляет тип данных и создает новый вариант функции. Если один и тот же тип данных используется несколько раз, то на все типы данных используется несколько раз, то на все типы данных создается один шаблон функции.

    При использовании шаблонов существует три больших недостатка:

    • шаблоны невозможно отлаживать.

    Разработка шаблонов обычно ведется так:

    1. разрабатывается класс или функция конкретного типа данных;

    2. этот класс или функция параметризуются, то есть создается шаблон.

    • существенно замедляется время компиляции. В больших проектах оно может доходить до 30-60 минут.

    • очень быстро растут размеры объектных модулей и библиотек на диске.

    Компиляция относительно большого проекта в отладочном режиме может потребовать до 10 ГБ.

    4.3. Перегрузка шаблонов функций

    Можно объявить несколько шаблонов функций с одним и тем же именем, а так же можно объявить комбинацию шаблонов и обычных функций с одинаковым именем:

    template T sqrt(T x)
    template

    complex sqrt(complex x);
    template

    double sqrt(double x);
    void Proc(complex z)

    {

    sqrt(2); // sqrt(int)

    sqrt(2.0); // sqrt(double)

    sqrt(z); // sqrt(comlex)

    }

    Шаблонные функции могут вызываться с явным указанием параметра шаблона:

    sqrt(2);

    или без него:

    sqrt(2);

    В этом случае применяется механизм разрешения перегрузки:

    • ищется набор специализации шаблонов функций, которые примут участие в разрешении перегрузки;

    • если могут быть вызваны два шаблона функций и один из них более специализирован, то только он и будет рассматриваться;

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

    • если и обычная функция, и специализация подходят одинаково хорошо, предпочтение отдается обычной функции;

    • если ни одного соответствия не найдено, или существует несколько одинаково хорошо подходящих вариантов, то выдается ошибка.

    В параметрах шаблонов допустимы стандартные значения, принимаемые по умолчанию:

    class Allocator

    {

    ... // malloc, free, calloc

    };
    template

    class basic_string

    {

    ...

    };
    basic_string mystr;

    basic_string commonstr;

    4.4. Специализации шаблонов

    Как правило, шаблон представляет единственное определение, которое применяется к различным аргументам шаблона. Это не всегда удобно, иногда существует необходимость использовать различные реализации в зависимости от типа.

    Например, надо для всех указателей использовать особую реализацию шаблона, а для всех базовых типов данных — обычную реализацию. Это делается с помощью специализации шаблона:

    template template <>

    class vector class vector

    { {

    ... ...

    }; };
    void Proc()

    {

    vector vi;

    vector vp;

    ...

    }

    Также может применяться частичная специализация шаблонов:

    template

    class vector : private vector

    {

    ...

    };
    template <>

    class vector

    {

    ...

    };

    Специализация шаблонов, как правило, используется для сокращения объема программного кода. Если шаблон создается для указателей на какие-то объекты и класс объекта не так важен, то при использовании обычных шаблонов без специализации возникает многократное дублирование одного и того же кода. Это связано с тем, что в машинных кодах работа со всеми указателями строится одинаково. Чтобы избежать дублирования кода в случае использования указателей следует создавать специализации шаблонов.

    4.5. Использование шаблонов при создании новых типов данных

    На основе шаблонов можно создать новый типа данных, использование которого позволит упростить программный код и избежать возможных ошибок.

    Существует два способа создания нового типа данных на базе шаблона:

    • можно воспользоваться оператором typedef:

    typedef basic_string string;

    Далее типом данных string можно пользоваться, забыв, что это шаблон.

    На самом деле для компилятора оператор typedef является как бы макроподстановкой, то есть при использовании типа string компилятор всегда подставляет значение типа basic_string.

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

    Например, выражение:

    string::iterator;

    на самом деле в объектном модуле выглядит так:

    basic_string::iterator;

    Оператор typedef по существу отличается от раздела type в Delphi. В С++ упрощение записи — макроподстановка.

    • наследование

    class string : public basic_string

    {

    ...

    };

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

    string::iterator;

    Таким образом, использование наследования позволяет решить проблему длины идентификаторов при работе с шаблонами.

    4.6. Стандартная библиотека шаблонов

    Перечислим, что содержится в стандартной библиотеке шаблонов:

    • Классы и шаблоны для организации потоков ввода/вывода

    В языке С++ вместо функций printf и scanf предлагается использовать объекты потоковых классов:

    std::cout;

    std::cin;

    Вывод осуществляется с помощью оператора сдвига:

    std::cout << "Hello!";

    int n;

    std::cin >> n;

    Чтобы перевести строку надо сделать следующую запись:

    std::cout << "Hello!" << std::endl; // или "\n"

    Объекты cout и cin являются экземплярами классов ostream и istream. Существуют также классы iostream (класс для ввода/вывода) и streambuf (позволяет выполнить буферизованный ввод/вывод).

    В программе не следует смешивать потоковый ввод/вывод с функциями printf и scanf. Если все же это происходит, То между блоками кода, использующими тот или иной подход, надо выполнять вызов функции fflush — сброс буферов.

    • Ссылки

    Для работы с ссылками подключается файл:

    #include

    Расширение .h у файлов стандартной библиотеки отсутствует.

    Среди строк наибольшей популярностью пользуются следующие классы:

    std::string

    std::wstring

    • Контейнеры

    В языке С++ существуют следующие контейнеры:

    std::vector // обычный массив

    std::list // список, реализация не уточняется.

    std::queve // FIFO

    std::deque // двунаправленная очередь

    std::stack // LIFO

    std::map // список объектов T, индексированных ключами K

    std::set // множество

    std::bitset // массив битов

    Для использования любого из шаблонов надо подключить заголовочный файл, название которого совпадает с названием подключаемого шаблона.

    В примере выше кроме параметра есть еще, как правило, три параметра. Здесь они преднамеренно опущены и для них заданы стандартные значения.

    Например, среди этих параметров есть allocator — это класс, который отвечает за создание или удаление элементов контейнера. Благодаря нему, можно создавать элементы в любой области памяти.

    • Итераторы

    #include

    Итератор — абстракция указателя на элемент контейнера.

    Пример использования:

    #include

    void strlen(const cahr *str)

    {

    const char *p = str;

    while(*p != 0)

    {

    ...

    ++p;

    }

    ...

    }

    Указатель в строке — это итератор по строке.

    Можно сказать, что в примере выше типы данных char* и const char* являются итераторами строки (обычной 0-терминированной).

    С помощью оператора typedef можно определить такой тип данных и затем их использовать:

    typedef char *iterator;

    typedef const char *const_iterator;

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

    Работа с этими типами данных происходит следующим образом:

    void Find(std::vector &v, const char *s)

    {

    typename std::vector::iterator it = v.begin();

    while (it != v.end())

    {

    const char *szName = (*it).Name;

    if (strcmp(szName, s) == 0)

    return true;

    ++it;

    }

    return false;

    }

    В стандартном контейнере существуют функции begin() и end(), которые возвращают соответственно итераторы на первый и последний элементы контейнера. Физически функция end() возвращает NULL.

    Если итератор адресует объект, то доступ к полям следует осуществлять с помощью оператора *. Допустимо использование и оператора ->, но он может быть переопределен и поэтому работа операторов *. и -> может отличаться. Переход к следующему элементу контейнера выполняется префиксным инкрементом ++it. Допустимо использование постфиксного оператора it++, но в последнем случае может возникнуть неоднозначность в том, что увеличивается на единицу — итератор или значение, возвращаемое итератором.

    • Алгоритмы

    #include

    #include

    В стандартной библиотеке существует несколько стандартных алгоритмов, которые следует изучить до того, как разрабатывать собственные алгоритмы над контейнерами.

    find()

    sort()

    *bsearch() // бинарный поиск

    *qsort() // быстрая сорировка

    Недостатком bsearch является то, что не возвращается место вставки элемента.

    • Утилиты

    #include

    #include

    #include

    #include

    В utility переопределен шаблон pair.

    В fuctional определены объекты функций — это такие объекты, которые могут использоваться как функции. Объекты функций применяются в алгоритмах. Например, выполняется обход контейнера и вызывается некоторая функция для каждого элемента контейнера. Вместо функции можно подставить объект-функцию. В этом и состоит смысл объекта-функции.

    memory — распределение памяти для контейнера (здесь находятся стандартные алгоритмы).

    В ctime находятся функции времени и даты.

    • Диагностика

    #include // классы исключительных ситуаций

    #include // макрос assert. Вместо него лучше использовать

    // исключительные ситуации

    #include // файлы обработки ошибок в стиле С, то есть функции,

    // которые возвращают коды ошибок

    • Локализация

    Поддержка различных языков:

    #include

    #include

    • Поддержка языка программирования С++

    Крайние граничные значения типов данных:

    #include

    #include

    #include // поддержка RTTI

    #include // поддержка исключительных ситуаций

    • Работа с числами

    #include

    #include

    #include

    #include

    #include



    1   ...   25   26   27   28   29   30   31   32   33


    написать администратору сайта