Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

hkjCJgcQqF

.pdf
Скачиваний:
2
Добавлен:
15.04.2023
Размер:
810.94 Кб
Скачать

using namespace std; template <class T> T max1( T t1, T t2 ) {

return ( t1 > t2 ? t1 : t2 );

template<> const char* max1< const char *>(const char* s1,const char*

s2 )

{ //определена явная специализация: return ( strcmp( s1, s2 ) > 0 ? s1 : s2 ); int main() {

//вызов конкретизированной функции: int max< int >( int, int ); int i = max( 10, 5 );

//вызов явной специализации:

//const char* max1< const char* >( const char*, const char* ); const char *p = max1( "hello", "world" );

cout << "i: " << i << " p: " << p << endl; return 0;

}

При объявлении или определении явной специализации шаблона функции нельзя опускать слово template и следующую за ним пару скобок <>.

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

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

Приведенная ниже небольшая программа иллюстрирует перегрузку шаблона функции.

#include <cstdlib> #include <iostream> using namespace std;

inline int const& max1(int const & a, int const& b) { return a < b ? b : a; }

template <class T>

inline T const& max1(T const& a, T const& b)

{

return a < b ? b : a;

}

template <class T>

inline T const& max1(T const & a, T const& b, T const& c)

{

return max1(max(a,b),c);

}

113

int main () { ::max1(7, 42, 68) ; ::max1(7.0, 42.0); ::max1('a' , 'b') ; ::max1(7, 42); ::max1<>(7, 42);

::max1<double>(7, 42) ; ::max1('a', 42.7); system("PAUSE"); return EXIT_SUCCESS;

}

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

Лабораторная работа № 4 Тема: Шаблоны функций в языке С++

Цель: Изучить использование шаблонов функции Краткие теоретические сведения

Алгоритм последовательного поиска принимает ключ и просматривает список элементов на предмет совпадения. Определим шаблонную функцию поиска:

//Заголовочный файл utils.h #include <string.h>

//используя ключ, ищет совпадение в массиве из n элементов.

//если совпадение найдено, возвращает индекс совпадения; иначе возвращает -1

template <class T>

int SeqSearch(T list[], int n, T key) { for(int i=0;i < n;i++)

if (list[i] == key)

return i;

// возвращает индекс совпадающего элемента

return -1;

// поиск неудачный; возвращает -1

}

// поиск в строковом массиве

int SeqSearch(char *list[], int n, char *key) { for(int i=0;i < n;i++)

// сравнить, используя строковую библиотечную функцию if (strcmp(list[i],key) == 0)

114

return i; // возвратить индекс совпадающей строки return -1; } // при неудаче возвратить -1

// сортировка n элементов массива а с использованием обменной //сортировки exchange sort algorithm

template <class T>

void ExchangeSort(T a[], int n) { T temp;

int i, j;

//выполнить n-1 проходов for (i = 0; i < n-1; i++)

//наименьшее из a[i+1]...a[n-1] поместить в a[i] for (j = i+1; j < n; j++)

if (a[j] < a[i]) {

// поменять местами a[i] и a[j] temp = a[i];

a[i] = a[j];

a[j] = temp; }

}

Программа представляет последовательный поиск для трех отдельных типов данных. Массив list инициализируется 10 целыми значениями. Перегрузка оператора используется с типом записи Student. (см. заголовочный файл student.h). Для массива строк используем нешаблонную функцию SegSearch с типом данных char.

///Заголовочный файл student.h

//запись о студенте, содержащая его ID и средний балл struct Student {

int studID; float gpa; };

//перегружает "==" , сравнивая id студента

int operator== (Student a, Student b) { return a.studID == b.studID; }

///Программа поиска #include <iostream> using namespace std;

//включить код шаблонной функции #include "utils.h"

//Объявление структуры Student и операции "==" для Student #include "student.h"

void main() {

// три массива с различными типами данных int list[10] = {5, 9, 1, 3, 4, 8, 2, 0, 7, 6};

Student studlist[3] = {{1000, 3.4},{1555, 2.6},{1625, 3.8}}; char *strlist[5] = {"zero","one","two","three","four"};

115

int i, index;

// эта запись используется в качестве ключа поиска Student studentKey = {1555, 0};

if ((i = SeqSearch(list,10,8)) >= 0)

cout << "Item 8 is found at index " << i << endl; else

cout << "Item 8 is not found" << endl; index = SeqSearch(studlist, 3, studentKey);

cout << "Student 1555 has gpa " << studlist[index].gpa << endl;

cout << "String 'two' is at index "

<< SeqSearch(strlist,5,"two") << endl;

///включите код

}

Задание

1

1.Напишите шаблон функции, возвращающей среднее арифметическое всех элементов массива. Аргументами функции должны быть имя и размер массива ( для размера используется тип int). В функции main() проверьте работу функции с массивами типа int, long, double, char. Создайте специализацию шаблонной функции, позволяющей подсчитывать среднее число символов в строках типа char * (массив строк char*).

2.Создайте функцию swaps(), обменивающуюся значениями двух аргументов, посылаемых ей. Сделайте из функции шаблон, чтобы она могла использоваться с любыми числовыми типами данных (int, char, float и т.п.). Напишите программу для тестирования функции. Добавьте в программу специализацию шаблона для char *.

Для справки:

void swaps(char* s1, char* s2) { int max_len;

max_len = (strlen(s1) >= strlen(s2)) ? strlen(s1) : strlen(s2);

char* temp = new char[max_len + 1]; strcpy(temp, s1);

strcpy(s1, s2); strcpy(s2, temp); }

3. Создайте функцию amax(), возвращающую значение максимального элемента массива. Аргументами функции должны быть адрес и размер массива. Сделайте из функции шаблон, чтобы она могла работать с массивом любого числового типа. В main() проверьте работу функции с разными типами массивов.

116

4.Напишите шаблон функции, всегда возвращающий свой аргумент, умноженный на два.

5.Напишите функцию

template <class T> int Max(T Arr[], int n);

которая возвращает индекс максимального значения в массиве.

2

1.Напишите шаблонную функцию cycle ( ) со следующим определением и проверьте ее:

template <class T>

void cycle (T & a, T

& b, T & c)

{

 

// заменяет значение a

на b и b на с и c на a

}

 

2.Напишите шаблонную функцию, которая перемещает значения данного массива произвольного размера следующим образом:

a[ 1]= a[ 0], a[2]= a[1], a[size-1] = a[size-2], a[0]= a[size-1]

Проверьте эту функцию с различными типами, в том числе с массивом char*- строк.

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

типа массива имеют элементы, являющиеся преобразуемыми по присвоению.

Контрольные вопросы

1.Шаблонный аргумент всегда начинается с ключевого слова _______ .

2.Шаблоны позволяют удобным способом создавать семейства:

переменных;

функций;

классов;

программ.

3.Истинно ли утверждение о том, что шаблон может иметь несколько аргументов?

4.Создание реальной функции из шаблона называется ______функции

Задание для самостоятельной работы

1.Реализуйте функцию template <class T>

int BinSearch( T A[], T key, int low, int hight);

которая выполняет бинарный поиск ключа в массиве А.

2.Напишите функцию template <class T> voidInsertOrder( T A[], int n, T elem);

которая вставляет elem в массив А, так что список сохраняет возрастающий порядок. Когда позиция находится для elem , нужно передвинуть все остальные элементы на одну позицию вправо.

117

Тестовое задание

Классы и объекты

1. В чем состоит различие (если таковое имеется) между следующими строками (Т здесь обозначает произвольный класс) ?

Tt;

Tt();

Tt(u);

Tt=u;

2.

class Date { int d, m, y; public:

void init_date ( int dd, int mm, int yy );

void add_month (int n );

 

void add_year (int n );

 

void add_day (int n );

};

2.Какие механизмы в языке С++ позволяют обеспечить инкапсуляцию объектов?

a.Модификаторы доступа

b.Виртуальные методы

c.Статические методы

d.Динамическое выделение памяти

e.Обработка исключений

3.Какие из следующих объявлений и определений статических членов ошибочны? Почему?

//example.h

class Example {

 

public:

 

static double rate=6.5;

// (a)

static const int vecSize=20;

//(b)

static vector<double> vec(vecSize);

//(c)

};

# include “example.h” double Example::rate;

vector<double> Example::vec;

4. К каким объявлениям относится имя Type при использовании в теле класса Exercize и в определении его функции-члена setVal() ? К каким объявлениям относится имя initVal при употреблении в определении функции-члена setVal() ? Укажите ошибки. Внесите необходимые изменения, чтобы в классе Exercize использовался глобальный typedef Type и глобальная функция initVal().

typedef int Type;

118

Type initVal(); class Exercize { public:

// ....

typedef double Type; Type setVal (Type ); Type initVal (); private:

int val; };

Type Exercise::setVal( Type parm ){ val=parm+initVal();

}

5. Пользуясь приведенным определением класса: class Account {

public: Account ();

explicit Account (const char*, double=0.0); // ....

}; Объясните, что происходит в результате следующих определений:

(а)Account acct;

(b)Account acct2=acct;

(c)Account acct3=”Мария Петрова”;

(d)Account acct4(“Анна Иванова”, 25000.00);

(e)Account acct5=Account (acct3);

6.Параметр копирующего конструктора может и не быть константным, но обязан быть ссылкой. Почему ошибочна такая инструкция:

Account:: Account (const Account rhs);

Приведите правильную реализацию копирующего конструктора. 7. Какие из приведенных инструкций неверны? Исправьте их.

(a)Account * parray[10]=new Account[10];

(b)Account iA[1024]={“Елена”, “Сергей”, “Антон”, “Мария”,

“Ольга” };

(c)string * ps=string[5] (“Елена”, “Сергей”, “Антон”, “Мария”,

“Ольга”);

(d)string as[]= *ps;

8. Что неверно в следующих определениях конструкторов ? Как бы вы исправили обнаруженные ошибки?

(a) Word::Word ( char *ps, int count=1): _ps(new char [strlen(ps)+1], _count(count)

{

119

if (ps ) strcpy(_ps, ps ); else {

_ps=0; _count=0;

}

};

(b)class CL1 { public :

CL1() { c.real(0.0); c.imag(0.0); s=”не установлено“; } // ....

private: complex<double> c; string s;

};

(c)class CL2 {

public :

CL2 ( map<string, location> *pmap, string key ): _text( key ), _loc

(*pmap)[key] ) { }

 

// ......

 

 

private:

 

location _loc;

 

string

_text;

 

};

 

 

9. Реализуйте для каждого

из данных классов копирующий конструктор,

конструктор по умолчанию и деструктор:

(a)

class BinStrTreeNode {

public:

 

// ....

 

private:

 

string _value;

 

int

_count;

 

BinStrTreeNode * _leftchild;

BinStrTreeNode *

_rightchild;

};

 

 

(b)class BinStrTree { public:

// ....

private:

BinStrTreeNode * _root;

};

(c) class iMatrix { public:

120

// ......

private:

int _rows; int _cols;

int * _matrix;

};

(d) class theBigMix {

public:

 

// .....

 

private:

 

BinStrTree

_bst;

iMatrix

_im;

string

_name;

vector<float> *_pvec;

};

10. Найдите в следующем фрагменте программы все места, где происходит почленная инициализация:

Point global;

Point func ( Point arg )

{

Point local =arg;

Point * heap= new Point (global ); * heap = local;

Point pa [ 4 ] = { local, * heap }; return * heap;

}

Перегруженные функции

1. Объясните, что происходит при разрешении перегрузки функции func(). Укажите какая функция вызывается для каждого фактического параметра. Если возможны неоднозначные вызовы, то укажите их.

extern void func (int );

extern void func ( long, int=0 );

int main ()

{

func (2L);

// (a)

func (0 , 0 ); // (b)

func (0 );

// (c)

func (3.14);

// (d)

// ....

 

}

2. Определите ошибки (если они есть) при выполнении преобразования параметров:

(a) void swap (int & , int & ); void func1( int i1, int i2) {

121

// ...

swap (i1, i2 0; //....

}

(b) int val;

void func (double &); int main () { func(val);

return 0;

}

3. Объясните, что происходит при разрешении перегрузки для вызова функции compute() внутри main (). Какая функция будет самой подходящей?

namespace primerABC { void compute ();

void compute( const void * );

}

using primerABC::compute; void compute( int );

void compute ( double, double=2.76 ); void compute ( char * , char*=0 );

int main () { compute (0); return 0;

}

Что будет, если using-объявление поместить внутрь функции main() перед вызовом compute() ?

Перегрузка операторов и преобразования, определенные пользователем

1. В следующей программе просто добавьте или удалите const там, где это стоит сделать (допустимы минимальные изменения и использование необходимых ключевых слов).

class Polygon

{

public:

Polygon () : area_(- 1) { } void AddPoint ( const Point pt )

{

InvalidateArea (); points_.push_back (pt); }

Point GetPoint ( const int i ) { return points_[i]; } int GetNumPoints () { return points_.size(); } double GetArea()

122

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]