НА ГЛАВНУЮ
Меню сайта
Категория
Ghost++ [1]
С++ [55]
Развлечение
ON - LINE
Опрос
Как часто вы играете в Доту?
Всего ответов: 288
Оbserver Ward

Онлайн всего: 1
Гостей: 1
Пользователей: 0


Друзья сайта
Заведи себе Бота
Hаша кнопка
Для обмена банерами , наша кнопка для размещения у вас на сайте

Клансайт USSR


Главная » Статьи » Программирование » С++

10. Шаблоны функций (3)
Упражнение 10.13

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

Упражнение 10.14

На какие объявления ссылаются имена display и SIZE в реализации max(LongDouble*,SIZE)?

// ---- exercise.h ----
void display( const void* );
typedef unsigned int SIZE;

template <typename Type>
  Type max( Type* array, SIZE size )
{
  Type max_val = array[0];
  for ( SIZE i = 1; i < size; ++i )
    if ( array[i] > max_val )
      max_val = array[i];

  display( "Maximum value found: " );
  display( max_val );
  return max_val;
}

// ---- user.h ----
class LongDouble { /* ... */ };
void display( const LongDouble & );
void display( const char * );
typedef int SIZE;

// ---- user.C ----
#include <exercize.h>
#include "user.h"
LongDouble ad[7];

int main() {
  // задать значения элементов массива ad

  // конкретизируется max( LongDouble*, SIZE )
  SIZE size = sizeof(ad) / sizeof(LongDouble);
  max( &ad[0], size );
}

10.10. Пространства имен и шаблоны функций А

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

// ---- primer.h ----
namespace cplusplus_primer {
   // определение шаблона скрыто в пространстве имен
   template <class Type>
   Type min( Type* array, int size ) { /* ... */ }
}

// ---- user.C ----
#include <primer.h>
int ai[4] = { 12, 8, 73, 45 };

int main() {
  int size = sizeof(ai) / sizeof(ai[0]);

  // ошибка: функция min() не найдена
  min( &ai[0], size );
 
  using cplusplus_primer::min; // using-объявление
  // правильно: относится к min() в пространстве имен cplusplus_primer
  min( &ai[0], size );
}

Что произойдет, если наша программа использует шаблон, определенный в пространстве имен, и мы хотим предоставить для него специализацию? (Явные специализации шаблонов рассматривались в разделе 10.6.) Допустим, мы хотим использовать шаблон min(), определенный в cplusplus_primer, для нахождения минимального значения в массиве объектов типа SmallInt. Однако мы осознаем, что имеющееся определение шаблона не вполне подходит, поскольку сравнение в нем выглядит так:

if ( array[i] < min_val )

В этой инструкции два объекта класса SmallInt сравниваются с помощью оператора <. Но этот оператор неприменим к объектам, если только не перегружен в классе SmallInt (мы покажем, как определять перегруженные операторы в главе 15). Предположим, что мы хотели бы определить специализацию шаблона min(), чтобы она пользовалась функцией compareLess() для сравнения двух подобных объектов. Вот ее объявление:

// функция сравнения объектов SmallInt
// возвращает true, если parm1 меньше parm2
bool compareLess( const SmallInt &parm1, const SmallInt &parm2 );

Как должно выглядеть определение этой функции? Чтобы ответить на этот вопрос, необходимо познакомиться с определением класса SmallInt более подробно. Данный класс позволяет определять объекты, которые хранят тот же диапазон значений, что и 8-разрядный тип unsigned char, т.е. от 0 до 255. Дополнительная функциональность состоит в том, что класс перехватывает ошибки переполнения и потери значимости. Во всем остальном он должен вести себя точно так же, как unsigned char. Определение SmallInt выглядит следующим образом:

class SmallInt {
public:
   SmallInt( int ival ) : value( ival ) {}
   friend bool compareLess( const SmallInt &, const SmallInt & );
private:
   int value; // член
};

В этом классе есть один закрытый член value, в котором хранится значение объекта типа SmallInt. Класс также содержит конструктор с параметром ival:

// конструктор класса SmallInt
SmallInt( int ival ) : value( ival ) {}

Его единственное назначение – инициализировать член класса value значением ival.
Вот теперь можно ответить на ранее поставленный вопрос: как должна быть определена функция compareLess()? Она будет сравнивать члены value переданных ей аргументов типа SmallInt:

// возвращает true, если parm1 меньше parm2
bool compareLess( const SmallInt &parm1, const SmallInt &parm2 ) {
   return parm1.value < parm2.value;
}

Заметим, однако, что член value является закрытым. Как может глобальная функция обратиться к закрытому члену, не нарушив инкапсуляции класса SmallInt и не вызвав тем самым ошибку компиляции? Если вы посмотрите на определение класса SmallInt, то заметите, что глобальная функция compareLess() объявлена как дружественная (friend). Если функция объявлена таким образом, то ей доступны закрытые члены класса. (Друзья классов рассматриваются в разделе 15.2.)
Теперь мы готовы определить специализацию шаблона min(). Она следующим образом использует функцию compareLess().

// специализация min() для массива объектов SmallInt
template<> SmallInt min<smallInt>( SmallInt* array, int size )
{
   SmallInt min_val = array[0];
   for (int i = 1; i < size; ++i)
     // при сравнении используется функция compareLess()
     if ( compareLess( array[i], min_val ) )
       min_val = array[i];

  print( "Minimum value found: " );
  print( min_val );

  return min_val;
}

Где мы должны объявить эту специализацию? Предположим, что здесь:

// ---- primer.h ----
namespace cplusplus_primer {
   // определение шаблона скрыто в пространстве имен
   template <class Type>
   Type min( Type* array, int size ) { /* ... */ }
}

// ---- user.h ----
class SmallInt { /* ... */ };
void print( const SmallInt & );
bool compareLess( const SmallInt &, const SmallInt & );

// ---- user.C ----
#include <primer.h>
#include "user.h"

// ошибка: это не специализация для cplusplus_primer::min()
template<> SmallInt min<smallInt>( SmallInt* array, int size )
{ /* ... */ }
// ...

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

// ---- user.C ----
#include <primer.h>
#include "user.h"

namespace cplusplus_primer {
  // специализация для cplusplus_primer::min()
  template<> SmallInt min<smallInt>( SmallInt* array, int size )
  { /* ... */ }
}
SmallInt asi[4];

int main() {
  // задать значения элементов массива asi с помощью функции-члена set()

  using cplusplus_primer::min; // using-объявление
  int size = sizeof(asi) / sizeof(SmallInt);
  // конкретизируется min(SmallInt*,int)
  min( &asi[0], size );
}

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

// ---- user.C ----
#include <primer.h>
#include "user.h"

// специализация для cplusplus_primer::min()
// имя специализации квалифицируется
namespace {
  template<> SmallInt cplusplus_primer::
  min<smallInt>( SmallInt* array, int size )
  { /* ... */ }
  // ...

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

Поместим содержимое заголовочного файла <exercise.h> из упражнения 10.14 в пространство имен cplusplus_primer. Как надо изменить функцию main(), чтобы она могла конкретизировать шаблон max(), находящийся в cplusplus_primer?
Упражнение 10.16

Снова обращаясь к упражнению 10.14, предположим, что содержимое заголовочного файла <exercise.h> помещено в пространство имен cplusplus_primer. Допустим, мы хотим специализировать шаблон функции max() для массивов объектов класса LongDouble. Нужно, чтобы специализация шаблона использовала функцию compareGreater() для сравнения двух объектов класса LongDouble, объявленную как:

// функция сравнения объектов класса LongDouble
// возвращает true, если parm1 больше parm2
bool compareGreater( const LongDouble &parm1,
const LongDouble &parm2 );
Определение класса LongDouble выглядит следующим образом:
class LongDouble {
public:
   LongDouble(double dval) : value(ival) {}
   friend bool compareGreater( const LongDouble &,
   const LongDouble & );
private:
   double value;
};

Напишите определение функции compareGreater() и специализацию max(), в которой эта функция используется. Напишите также функцию main(), которая задает элементы массива ad, а затем вызывает специализацию max(), доставляющую его максимальный элемент. Значения, которыми инициализируется массив ad, должны быть получены чтением из стандартного ввода cin.
10.11. Пример шаблона функции

В этом разделе приводится пример, показывающий, как можно определять и использовать шаблоны функций. Здесь определяется шаблон sort(), который затем применяется для сортировки элементов массива. Сам массив представлен шаблоном класса Array (см. раздел 2.5). Таким образом, шаблоном sort() можно пользоваться для сортировки массивов элементов любого типа.

В главе 6 мы видели, что в стандартной библиотеке C++ определен контейнерный тип vector, который ведет себя во многом аналогично типу Array. В главе 12 рассматриваются обобщенные алгоритмы, способные манипулировать контейнерами, описанными в главе 6. Один из таких алгоритмов, sort(), служит для сортировки содержимого вектора. В этом разделе мы определим собственный "обобщенный алгоритм sort()” для манипулирования классом Array, упрощенной версии алгоритма из стандартной библиотеки C++.
Шаблон функции sort() для шаблона класса Array определен следующим образом:

template <class elemType>
   void sort( Array<elemType> &array, int low, int high ) {
      if ( low < high ) {
         int lo = low;
         int hi = high + 1;
         elemType elem = array[lo];
         for (;;) {
            while ( min( array[++lo], elem ) != elem && lo < high ) ;
               while ( min( array[--hi], elem ) == elem && hi > low ) ;
                  if (lo < hi)
                     swap( array, lo, hi );
                  else break;
         }
         swap( array, low, hi );
         sort( array, low, hi-1 );
         sort( array, hi+1, high );
      }
   }

В sort() используются две вспомогательные функции: min() и swap(). Обе они должны определяться как шаблоны, чтобы иметь возможность обрабатывать любые типы фактических аргументов, с которыми может быть конкретизирован шаблон sort(). min() определена как шаблон функции для поиска минимального из двух значений любого типа:

template <class Type>
   Type min( Type a, Type b ) {
         return a < b ? a : b;
   }
   swap() – шаблон функции для перестановки двух элементов массива любого типа:
   template <class elemType>
   void swap( Array<elemType> &array, int i, int j )
   {
      elemType tmp = array[ i ];
      array[ i ] = array[ j ];
      array[ j ] = tmp;
   }

Убедиться в том, что функция sort() действительно работает, можно с помощью отображения содержимого массива после сортировки. Поскольку функция display() должна обрабатывать любой массив, конкретизированный из шаблона класса Array, ее тоже следует определить как шаблон:

#include <iostream>

template <class elemType>
   void display( Array<elemType> &array )
{ //формат отображения: < 0 1 2 3 4 5 >
   cout << "< ";
   for ( int ix = 0; ix < array.size(); ++ix )
   cout << array[ix] << " ";
   cout << ">\n";
}

В этом примере мы пользуемся моделью компиляции с включением и помещаем шаблоны всех функций в заголовочный файл Array.h вслед за объявлением шаблона класса Array.
Следующий шаг – написание функции для тестирования этих шаблонов. В sort() поочередно передаются массивы элементов типа double, типа int и массив строк. Вот текст программы:

#include <iostream>
#include <string>
#include "Array.h"

double da[10] = {
   26.7, 5.7, 37.7, 1.7, 61.7, 11.7, 59.7,
   15.7, 48.7, 19.7 };

int ia[16] = {
   503, 87, 512, 61, 908, 170, 897, 275, 653,
   426, 154, 509, 612, 677, 765, 703 };

string sa[11] = {
   "a", "heavy", "snow", "was", "falling", "when",
   "they", "left", "the", "police", "station" };

int main() {
   // вызвать конструктор для инициализации arrd
   Array<double> arrd( da, sizeof(da)/sizeof(da[0]) );

   // вызвать конструктор для инициализации arri
   Array<int> arri( ia, sizeof(ia)/sizeof(ia[0]) );

   // вызвать конструктор для инициализации arrs
   Array<string> arrs( sa, sizeof(sa)/sizeof(sa[0]) );
   cout << "sort array of doubles (size == "
        << arrd.size() << ")" << endl;
   sort(arrd, 0, arrd.size()-1 );
   display(arrd);
   cout << "sort array of ints (size == "
        << arri.size() << ")" << endl;
   sort(arri, 0, arri.size()-1 );
   display(arri);
   cout << "sort array of strings (size == "
        << arrs.size() << ")" << endl;
   sort(arrs, 0, arrs.size()-1 );

   display(arrs);
   return 0;
}

Если скомпилировать и запустить программу, то она напечатает следующее (эти строки искусственно разбиты на небольшие части):

sort array of doubles (size == 10)
< 1.7 5.7 11.7 14.9 15.7 19.7 26.7
37.7 48.7 59.7 61.7 >

sort array of ints (size == 16)
< 61 87 154 170 275 426 503 509 512
612 653 677 703 765 897 908 >

sort array of strings (size == 11)
< "a" "falling" "heavy" "left" "police"    "snow"
"station" "the" "they" "was" "when"    >

В числе обобщенных алгоритмов, имеющихся в стандартной библиотеке C++ (и в главе 12), вы найдете также функции min() и swap(). В главе 12 мы покажем, как их использовать.
Категория: С++ | Добавил: r2d2 (29.09.2011)
Просмотров: 820 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Born in Ussr
Залогиниться
Турниры

/j clan ussr /j clan cccp