Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП_ Лекция №06 - Шаблоны. Основы обобщенного программирования..docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
138.46 Кб
Скачать

Пример: собственная шаблонная реализация std::initializer_list

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

Ниже представлено объявление и реализация шаблона класса такого списка, по составу очень близкая к стандартному средству std::initializer_list. Объект такого класса позволяет удовлетворять минимальным требованиям интервального цикла for.

initializer_list.hpp

#ifndef _INITIALIZER_LIST_HPP_

#define _INITIALIZER_LIST_HPP_

/*****************************************************************************/

template< typename T >

class InitializerList

{

/*-----------------------------------------------------------------*/

public:

/*-----------------------------------------------------------------*/

// Конструктор по умолчанию

InitializerList ();

// Конструктор, принимающий интервал указателей на данные массива

InitializerList ( const T * _pBegin, const T * _pEnd );

/*-----------------------------------------------------------------*/

// Метод доступа к указателю на начало массива

const T * begin () const;

// Метод доступа к указателю на элемент массива, следующий за последним

const T * end () const;

// Метод подсчета размера массива

size_t size () const;

/*-----------------------------------------------------------------*/

// Вспомогательный синоним типа для хранимого элемента данных

typedef T value_type;

/*-----------------------------------------------------------------*/

private:

/*-----------------------------------------------------------------*/

// Указатель на начало массива

const T * m_pBegin;

// Указатель на элемент массива, следующий за последним

const T * m_pEnd;

/*-----------------------------------------------------------------*/

};

/*****************************************************************************/

// Конструктор по умолчанию

template< typename T >

InitializerList< T >::InitializerList ()

: m_pBegin( nullptr )

, m_pEnd( nullptr )

{}

/*****************************************************************************/

// Конструктор, принимающий интервал указателей на данные массива

template< typename T >

InitializerList< T >::InitializerList ( const T * _pBegin, const T * _pEnd )

: m_pBegin( _pBegin )

, m_pEnd( _pEnd )

{}

/*****************************************************************************/

// Метод доступа к указателю на начало массива

template< typename T >

const T * InitializerList< T >::begin () const

{

return m_pBegin;

}

/*****************************************************************************/

// Метод доступа к указателю на элемент массива, следующий за последним

template< typename T >

const T * InitializerList< T >::end () const

{

return m_pEnd;

}

/*****************************************************************************/

// Метод подсчета размера массива

template< typename T >

size_t InitializerList< T >::size () const

{

return m_pEnd - m_pBegin;

}

/*****************************************************************************/

#endif // _INITIALIZER_LIST_HPP_

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

Ниже приведена тестовая программа, где созданный шаблон класса инстанцируется трижды с различными типами данных (int, double, std::string). Содержимое списков перебирается интервальным циклом for и последовательно выводится на экран, при этом, для всех типов списков используется один и тот же вспомогательный шаблон глобальной функции printList.

test_initializer_list.cpp

/*****************************************************************************/

#include "initializer_list.hpp"

#include <string>

#include <iostream>

/*****************************************************************************/

// Шаблон вспомогательного метода для вывода списка на экран

template< typename L >

void printList ( L _l )

{

// Выводим количество элементов

std::cout << "List(" << _l.size() << "): ";

// Перебираем все элементы и выводим их на экран последовательно

for ( typename L::value_type y : _l ) // используем вложенный синоним value_type

std::cout << y << ' ';

std::cout << std::endl;

}

/*****************************************************************************/

int main ()

{

// Массив целых чисел

int data1[] = { 1, 2, 3, 4, 5 };

int size1 = sizeof ( data1 ) / sizeof( int );

// Массив действительных чисел

double data2[] = { 1.0, 2.0, 3.0, 4.0 };

int size2 = sizeof( data2 ) / sizeof( double );

// Массив строк

std::string data3[] = { "A", "B", "C" };

int size3 = sizeof( data3 ) / sizeof( std::string );

// Формируем по объекту InitializerList для каждого из массивов

InitializerList< int > list1( data1, data1 + size1 );

InitializerList< double > list2( data2, data2 + size2 );

InitializerList< std::string > list3( data3, data3 + size3 );

// Выводим все три списка на экран

printList( list1 );

printList( list2 );

printList( list3 );

}

/*****************************************************************************/

Единственным существенным недостатком по сравнению со стандартным элементом, очевидно, является отсутствие поддержки неявного конструирования при использовании агрегатных литералов для временных массивов (выражения в стиле {1,2,3} без объявления массива явно). Вмешаться в данную логику компилятора по выбору стандартной реализации списка инициализаторов не представляется возможным.