ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Categories:

Тестирование шаблона функции

Тут используются: язык программирования C++, операционная система «Windows 7», среда разработки «Visual Studio Community 2017».

Задание к первому упражнению 14 главы «Шаблоны и исключения» учебника Лафоре (стр.678):

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


Если вы прочли 14 главу учебника, то это упражнение выполнить несложно, потому что в самой главе есть несколько примеров, которые можно взять за основу решения, немного их изменив. Я так и сделал и моё решение в целом совпало с примером решения из учебника (оно приведено на стр.891). Вот какой шаблон функции у меня получился:
template <class T>
T average(T* array, int size)
{
    T sum = 0;                     // узкое место (см. пояснения ниже)
    for (int j = 0; j < size; j++)
        sum += array[j];
    return sum / size;
}

Я подготовил следующие данные для тестирования этого шаблона функции (добавил к требуемым в упражнении еще тестовый массив типа wchar_t, потому что мне нравится работать с Юникодом):
int intArr[] = { 2, -3, 5, 7 };
long lonArr[] = { 13L, 7L, 11L, -5L, 17L };
double dubArr[] = { 12.1, 3.024, -0.56 };
char chaArr[] = "Ilya";
    // или char chaArr[] = { 'I', 'l', 'y', 'a', '\0' };
    // или char chaArr[] = { 73, 108, 121, 97, 0 };
wchar_t wchArr[] = L"Илья";
    // или wchar_t wchArr[] = { L'И', L'л', L'ь', L'я', L'\0' };
    // или wchar_t wchArr[] = { 1048, 1083, 1100, 1103, 0 };
В комментариях даны альтернативные способы инициализации символьного массива. Из-за того, что строковые литералы "Ilya" и L"Илья" являются нуль-терминированными строками, их длина составляет 5 символов, если считать символ '\0', обозначающий конец строки.

В принципе, базовые типы char и wchar_t — это числовые типы. Первый из них представляет числа в диапазоне 0..255 (1 байт). Второй из них в операционных системах «Windows» занимает 2 байта и поэтому представляет числа в диапазоне 0..65535. В символы эти числа превращаются, когда дело доходит до их интерпретации. То есть одно и то же число может считаться разным символом в зависимости от кодировки, в которой оно интерпретируется.

В моей системе в базовом типе char первые 128 чисел диапазона 0..255 (то есть числа диапазона 0..127) интерпретируются согласно таблице кодов ASCII, которая содержит буквы латинского алфавита. Начало упомянутого выше Юникода тоже совпадает с таблицей кодов ASCII. То есть можно утверждать, что приведенные выше тестовые наборы чисел (кодов символов) для типов char и wchar_t соответствуют кодам символов Юникода:
{ 73, 108, 121, 97, 0 }
{ 1048, 1083, 1100, 1103, 0 }
Это коды в привычной нам десятичной системе счисления. Чаще используют коды в шестнадцатеричной системе. То есть возможен еще один способ инициализации наших символьных массивов, с помощью шестнадцатеричных кодов символов Юникода. Или с помощью восьмеричной системы... Кстати, есть очень удобный сайт, на котором можно просмотреть таблицу кодов Юникода: https://unicode-table.com/ru/. В поисковой строке этого сайта для поиска страницы нужного символа можно задать либо сам символ, либо его код (хоть в десятичной системе, хоть в шестнадцатеричной).

Итак, рассчитаем, какой результат должна вернуть функция, реализованная компилятором из указанного выше шаблона функции для указанных выше тестовых массивов:
int: (2 – 3 + 5 + 7) / 4 = 11 / 4 = 2,75 => 2
long: (13 + 7 + 11 – 5 + 17) / 5 = 43 / 5 = 8,6 => 8
double: (12,1 + 3,024 – 0.56) / 3 = 14,564 / 3 = 4,8546666(6) => 4,85467
char: (73 + 108 + 121 + 97 + 0) / 5 = 399 / 5 = 79,8 => 79 => заглавная латинская буква «O»
wchar_t: (1048 + 1083 + 1100 + 1103 + 0) / 5 = 4334 / 5 = 866,8 => 866 => комбинируемая подстрочная двойная стрелка вправо

Знак '=>' в этих выражениях означает «следовательно». В четырех тестовых случаях из пяти функция должна вернуть целое число, а после деления получается дробь. Как известно, в таких случаях в C++ (неявное приведение базовых типов) дробная часть просто отбрасывается, а не округляется. Поэтому из 2,75 получается 2, из 8,6 получается 8 и так далее.

Результаты работы функций выводим в текстовую консоль с помощью поточного вывода (std::wcout). В случае тестового массива типа double функция возвращает бесконечную периодическую десятичную дробь. По умолчанию в поток выводится число с точностью до 6 знаков (считаются знаки и до запятой, и после). При этом, если в числе больше 6 знаков, то число округляется до 6 знаков. Именно поэтому бесконечная периодическая десятичная дробь 4,8546666(6) выводится в текстовую консоль как 4,85467. В случае необходимости точность можно увеличить с помощью манипулятора потока std::setprecision.

При поточном выводе значения типа char или wchar_t это число интерпретируется как символ, поэтому в текстовую консоль выводится символ с соответствующим кодом из таблицы Юникода. Символ с кодом 79 — это заглавная латинская буква «O». Символ с кодом 866 в таблице Юникода называется «Комбинируемая подстрочная двойная стрелка вправо» (по-английски «Combining Double Rightwards Arrow Below»). Этот символ не предназначен для отображения по отдельности от других знаков, так как он входит в раздел Юникода «Комбинируемые диакритические знаки» (см. в википедии, что такое «диакритические знаки»). Обычно в шрифтах, в которых нет отображения для данного символа, вместо символа показывается специальный знак: например, знак вопроса ? или знак в виде пустого квадратика (см. иллюстрации ниже).

Полный текст программы: 14chapter_01exercise.cpp.

Узкое место. После запуска данной программы с шаблоном функции, описанным в начале поста, видно, что в случае массива типа char она работает не так, как ожидалось. Это происходит потому, что переменная sum реализованной из шаблона функции тоже принимает в случае такого массива тип char. Но, как можно увидеть из вышеприведенных тестовых вычислений, сумма при указанных тестовых данных должна принять значение 399, а это значение выходит за пределы возможного диапазона 0..255 для типа char. Отсюда начинается ошибка в вычислениях, которая и приводит к неверному результату, выдаваемому функцией, хотя эта ошибка не приводит к прекращению работы программы. Чтобы исправить эту ошибку, я задал переменной sum тип double.

Результат работы программы (шрифт в консоли — «Terminal»):

Результат работы программы (шрифт в консоли — «Lucida Console»):


Кстати, при компиляции данной программы интересно наблюдать, когда и как компилятор создает реализации (по-английски «instantiation») функций по описанному в программе шаблону. Это происходит, когда компилятор находит в тексте программы вызов шаблонной функции; он создает новую реализацию функции, ориентируясь на типы аргументов, заданных при вызове функции.
Tags: Образование, Программирование
Subscribe

  • Эйфелева башня, парижский флёр

    Замечательная картина австралийской художницы Изабеллы Каролевич (Isabella Karolewicz), которая называется «Eiffel Tower, Paris Flair»:…

  • Кэнтаро Миура умер

    Оказывается, 6 мая этого ( 2021) года умер от разрыва аорты японский мангака Кэнтаро Миура. Земля пухом. Ему было всего лишь 54 года. Я его знал…

  • Мифы о большом взрыве, Олег Верходанов

    Никогда не думал, что научная лекция может собрать на ютубе миллион просмотров. И вот она, эта лекция. Ничего не понятно, но очень интересно,…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments