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

  • Параметры

  • Понятие

  • Алгоритмизации


    Скачать 1.15 Mb.
    НазваниеАлгоритмизации
    Дата27.09.2022
    Размер1.15 Mb.
    Формат файлаdocx
    Имя файла12_100229_1_124427 (1).docx
    ТипДокументы
    #700459
    страница45 из 67
    1   ...   41   42   43   44   45   46   47   48   ...   67

    Флажки


    Помимо манипуляторов для управления выводом данных используются специальные флажки, принадлежащие классу ios, которые также позволяют формировать потоки вывода.

    Установить флажок позволяет функцияsetiosflags(ios:: flag);

    Снять флажок позволяет функция resetiosflags(ios:: flag);

    Причем можно установить сразу несколько флажков, используя для этого побитовую операцию «|» (поразрядное ИЛИ) для их объединения в одну группу.

    Следующий пример показывает приемы работы с некоторыми флажками механизма вывода потоками.

    #include #include #include void main(void) {

    int a = 157;

    cout< :: showbase)< “<

    < endl;

    /* showbase показать, в какой системе счисления выводится число. На экране: 157 0235 0х9d */

    double a1 = 12.99, a2 = 15;

    cout << setiosflags(ios :: showpoint | ios :: fixed)

    /* showpointпечатать десятичную точку, fixed выводить в форме с фиксированной десятичной точкой */

    << setprecision(2) << setfill(‘*’) << setiosflags(ios :: right)

    // right выравнивать вывод по правому краю (по левому left)

    << a1 << setw(10) << a1

    << a2 << setw(10) << a2 << endl;

    // На экране: a1 *****12.99 a2 *****15.00

    double pi = 3.14159;

    cout << Pi << setw(15) << setfill(‘_’)

    // Символ заполнения ‘_’ знак подчеркивания

    << setiosflags(ios :: showpos | ios :: scientific)

    << setprecision(5) << pi << endl;

    /* showpos явно показать знак «+», scientific вывод в форме с плавающей десятичной точкой. На экране: Pi _ _ _ +3.14159e+00 */

    }
    В заключение отметим, что можно создавать свои собственные манипуляторы, которые будут выполнять запрограммированные действия.
      1. Проблемаввода-выводакириллицывсредеVisualC++


    Работа в среде VisualC++ 6.0 (в режиме консольных приложений) сопряжена с определенными неудобствами. Например, попытка вывести фразу на русском языке, как стандартными функциями вывода, так и с помощью ввода-вывода потоками, терпит неудачу. Создадим в средеVisual C++ 6.0 консольное приложение и наберем следующий текст:

    #include int main()

    {

    cout << "Welcome to C++ !" << endl;

    cout << "Добро пожаловать в C++ !" << endl; return 0;

    }

    В результате на экране получим нечто следующее:

    WelcometoC++!

    юсЕюяюцрыютрЄ№тC++! Press any key to continue

    То есть вместо фразы на русском языке получается бессмысленный набор символов. Это вызвано различными стандартами кодировки символов кириллицы в операционных системах MS DOS и Windows.

    Весь ввод-вывод в консольном окне идет в кодировке стандарта ASCII. Данный стандарт является международным только в первой половине кодов, т.е. для кодов от 0 до 127, а вторая половина кодов от 128 до 255 предназначена для национальных шрифтов. Так, например, в бывшем СССР помимо альтернативной кодировки ГОСТа (Alt), использовались – основная кодировка ГОСТа (Mai), болгарская кодировка (MIC), кодировка КОИ-8 (KOI), у которых символы кириллицы имеют разные коды. Сейчас в России– альтернативная кодировка ASCII.

    Текст же в исходных файлах, набираемый в текстовом редакторе Visual C++, имеет кодировку в стандарте ANSI. Данный стандарт в первой половине совпадает с альтернативной кодировкой ASCII, а во второй – отличается, так как разработчикиVisual решили, что консольное приложение должно имитировать работу в среде MSDOS и оставили альтернативную кодировку ASCII.

    Для нормального вывода строки, содержащей буквы русского алфавита, надо использовать функцию CharToOem, предназначенную для преобразования символов с кодировкой ANSI в кодировку ASCII. Аналогично, если в программе есть консольный ввод текста и этот текст в дальнейшем надо сохранять в документах (файлах) с кодировкой ANSI, то перед сохранением нужно воспользоваться функцией обратного преобразования OemToChar. Эти функции декларированы в заголовочном файле windows.h.

    С учетом сказанного выше можно предложить следующую программу корректного вывода информации на русском языке:

    #include #include

    char* Rus(const char* text); char bufRus[255];

    int main()

    {

    char s[] = "Минск!", ss[100];

    cout << Rus("Город ") << Rus(s) <
    cin >> ss;

    cout << Rus(" Строка: ") << ss << endl; return 0;

    }

    char* Rus (const char* text)

    {

    CharToOem(text, bufRus); return bufRus;

    }
    Результат программы может быть следующим:

    ГородМинск!

    Введистроку:Москва! Строка: Москва!

    Таким образом, для решения проблемы с русским языком в консольном выводеVisualC++ 6.0 создана небольшая функцияRus, которая обращается к функции CharToOem, передает ей для преобразования полученный через свой параметр текст на русском языке и возвращает указатель на преобразованную строку. В качестве временного хранилища используется глобальный символьный массив bufRus. Использовать функцию просто: везде вместо строковых объектов (строковых констант и переменных) в программах нужно писать Rus(строковый объект).

    Непосредственное использование функции CharToOem, например, в стандартных функциях вывода данных недопустимо, так как возвращает результат типа BOOL, а результат преобразования размещает по адресу своего второго аргумента. Поэтому и была создана эта небольшая пользовательская функция, которая имеет единственное ограничение: функцию Rusнельзя использовать в цепочке операций << более одного раза, так как для различных компиляторов и режимов оптимизации может быть получен неверный результат.
      1. Операцииnewиdelete


    В языке С++ для захвата и освобождения памяти используется более простой механизм – операции new и delete. Рассмотрим эти операции на простых примерах:

    1. type*p= newtype(значение); захват участка памяти размером sizeof(type), путем установки на него указателя, и запись в эту область указанного значения;

    ...

    deletep; освобождение захваченной памяти.

    1. type*p= newtype[n]; – захват памяти на nпоследовательно размещенных объектов, возвращает указатель на начало участка ОП размером n*sizeof(type); используется для создания массива;

    . . .

    delete[]p; освобождение всей захваченной памяти.

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

    Квадратные скобки в операцииdelete[ ] при освобождении памяти, занятой массивом, обязательны. Их отсутствие может привести к непредсказуемым результатам.

    Примерсозданияодномерногодинамическогомассива


    Для примера приведем участок кода программы для одномерного динамического массива с использованием операций new и delete.

    Напомним, что результатом операции new является адрес начала области памяти для размещения данных, указанного количества и типа. При нехватке памяти результат равен NULL.

    double *x; int i, n;

    puts(" Введите размер массива: "); scanf(“%d”, &n);

    x = new double [n] ; if (x = = NULL) {

    puts(" Ошибка ! "); return;

    }

    for (i=0; i i++) // Ввод элементов массива

    scanf(“%lf”, &x[i]);

    // Обработка массива

    delete [ ]x; // Освобождение памяти


    Примерсозданиядвухмерногодинамическогомассива


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

    . . .

    int **m, n1, n2, i, j;

    puts(" Введите размеры массива (строк, столбцов): "); scanf(“%d%d”, &n1, &n2);

    m = new int*[n1]; // Захват памяти для указателей А(n1=3) for (i=0; i
    *(m+i) = new int[n2]; for ( i=0; i
    for ( j=0; j j++)

    m[i] [j] = i+j; // *(*(m+i)+j) = i+j;

    . . .

    for ( i=0; i i++) // Освобождение памяти

    delete []m[i]; delete []m;

    . . .


      1. Дополнительныевозможностиприработеспользовательскими функциями



    Параметрысозначениямипоумолчанию

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

    В качестве значений параметров по умолчанию могут использоваться константы или константные выражения.

    Параметр по умолчанию проходит проверку типа во время описания функции и вычисляется во время ее вызова.

    Пример участка кода функции, определяющей сумму переменных отношений от 2-х до 5-ти:

    . . .

    int sum(int a, int b, int c=0, int d=0, int e=0) { // 0 умалчиваемые значения

    return (a+b+c+d+e);

    }

    int main ()

    {

    int x1=1, x2=2, x3=3, x4=4, x5=5;

    int y2, у3, у4, у5;

    у2= Sum (х1, х2); // Работают все умалчиваемые значения; у3= Sum (х1, х2, х3); // – два последних значения;

    у4= Sum (х1, х2, х3, х4); // одно последнее значение; у5= Sum (х1, х2, х3, х4, х5)

    . . .

    return 0;

    }

    Таким образом:

        1. Умалчиваемое значение аргумента функции задается при его

    объявлении в заголовке функции.

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

        2. При обращении пропуск умалчиваемых параметров в списке недопустим, т.е. для получения значения x1 + x2 + x3 + x5 вызов функции Sum (х1, х2, х3, х5); приведет к ошибочному результату.

    Правильным будет обращение Sum(x1, x2, x3, 0, x5);

    Перегрузкафункций


    В языке С++ реализована возможность использования одного идентификатора для функций, выполняющих различные действия над различными типами данных, в результате чего можно использовать несколько функций с одним и тем же именем, но с разными списками параметров, как по количеству, так и по типу.

    Такие функции называют перегруженными, а сам механизм –

    перегрузкафункций.

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

    Поиск функции, которую надо вызвать, осуществляется за три отдельных шага:

    1. Поиск функции с точным соответствием параметров и ее использование, если она найдена.

    2. Поиск соответствующей функции, используя встроенные преобразования типов данных.

    3. Поиск соответствующей функции, используя преобразования, определенные пользователем.

    Примерперегрузкифункций


    Приведем пример функции S1 с двумя параметрами х, у, работающая в зависимости от типа передаваемых аргументов, следующим образом:

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

      • если тип параметров long, функция S1 перемножает их значения и возвращает полученное произведение;

      • если тип параметров вещественный, функция S1 делит их значения и возвращает частное от деления.

    # include int S1 (int x, int y) {

    return x+y;

    }

    long S1 (long x, long y) { return x*y;

    }

    double S1 (double x, double y) { return x/y;

    }

    int main ()

    {

    int a = 1, b = 2, c; long i = 3, j = 4, k;

    double x = 10, y = 2, z; c=S1(a, b);

    k=S1(i, j);

    z=S1(x, y),

    printf("\n c = %d; k = %ld; z = %lf . \n", c, k, z); return 0;

    }

    В результате получим:

    c=3;k =12;z=5.000000.

    Функцииспеременнымчисломпараметров


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

    voidf1(inta, doubleb, …);

    Такая запись указывает компилятору на то, что за обязательными фактическими аргументами для параметровaи bмогут следовать, а могут и не следовать другие аргументы при вызове этой функции.

    Перечислим основные особенности использования данного механизма.

    1. Используется несколько макрокоманд для доступа к параметрам таких функций, это:

    va_listи va_start макрокоманды подготовки доступа к параметрам;

    va_arg использование параметров;

    va_end отчистка перед выходом.

    Они объявлены в заголовочном файле stdarg.h.

    1. Такая функция должна иметь минимум один параметр

    (именованный) для передачи ей количества передаваемых аргументов.

    1. Для макроса va_startнеобходимо передать два аргумента имя списка параметров, который задает va_list и их количество.

    2. Нарушать указанный порядок макрокоманд нельзя. Иначе можно получить непредсказуемые последствия.

    3. Для макроса va_arg нужно помимо имени списка параметров передать и предполагаемый тип. При несоответствии типов – ошибка. Использование многоточий полностью выключает проверку типов параметров. Многоточие необходимо, только если изменяются и число параметров, и их тип.

    Следующий пример иллюстрирует эту возможность.

    #include #include

    void f1(double s, int n ...) { int v;

    va_list p; va_start(p, n);

    printf(" \n Double S = %lf ", s); for(int i=1; i<=n; i++) {

    v = va_arg(p, int);

    printf("\n Argument %d = %d ", i, v);

    }

    va_end(p);

    }

    void main(void) {

    f1(1.5, 3, 4, 5, 6);

    }

    В результате получим:

    DoubleS=1.500000 Argument 1 = 4

    Argument2=5

    Argument3=6

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



    Понятиешаблонафункции

    Параметризация определений функций позволяет сократить трудоемкость программирования и повысить лаконичность текста.

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

    Компилятор по заданному в качестве аргумента конкретному типу данных на основе определения шаблона автоматически порождает соответствующие экземпляры или «представители» функций.

    Рассмотрим необходимость создания шаблона функции на конкретном примере.

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

    Существуют следующие варианты решения данной задачи.

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

    int Max(int x, int y) {

    return (x>y) ? x : y;

    }

    double Max(double x, double y) {

    return(x>y)? x : y;

    }
    int i, j, k;
    . . .

    k = Max(i, j); // Первая функция

    double a, b, d;

    d = Max(a, b); // Вторая функция

    . . .

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

      1. Вторая возможность воспользоваться макросом: #define Max(x, y) ((x>y) ? x : y )

    В этом случае недостатки:

    снижается надежность, т.к. при использовании макросов не работает проверка типов аргументов;


    Max;

      • данная подстановка будет выполнена во всех местах, где встретится




      • в данной ситуации лучше воспользоваться механизмом шаблонов,

    основанном на параметризации. Данная параметризация осуществляется при помощи обобщенного определения этой функции шаблоном (template), в котором тип параметров определяют символическим идентификатором (например буквой).

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

    Формат шаблона функции:

    templateсписокпараметровшаблона декларация функции;

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

    Элемент декларацияфункции обычное определение (или описание – прототип) функции. В списке ее параметров необходимо упомянуть хотя бы один раз типы параметров из списка шаблона.

    Примеры отношений между параметрами шаблона и функции:

    1. Шаблон может иметь единственный параметр: template

    void f1(T par) {

    код функцииf1

    }

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

    Функция может иметь любые типы параметров как параметризованные, так и стандартно декларированные.

    1. Шаблон функции с частично параметризованными параметрами:

    template T>

    void f2(T par, int x, int y) {

    код функцииf2

    }

    1. Шаблон может иметь несколько параметризованных параметров с разными символическими идентификаторами:

    template T2, class T1> void f3(T1 par1, T2 par2) {

    код функции f3

    }

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

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

    Пример нахождения максимума для разных типов аргументов: #include

    template T>

    T max(T x, T y) {

    return(x>y)? x : y;

    }

    void main()

    {

    int i = 2, j = 3;

    double x = 1.5, y = 2.5;

    cout <<” I = ”< J = ”< Max = ”< <<” X = ”< Y = ”< Max = ”<
    }
    Результат выполнения программы:

    i=2j=3Max= 3

    X=1.5y=2.5Max =2.5

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

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


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

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

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

    Перегрузка шаблона создается путем явного его определения

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

    Например, для строк шаблон-функция mахне подходит, так как все операции над строками производятся только при помощи библиотечных функций, декларированных в заголовочном файле string.h.

    Шаблон функции поиска максимума будет иметь следующий вид: template

    T max(T x, T y) {

    return (x>y)? x : y;

    }

    Специализированная функция выбора максимальной из двух строк: char* max(char* x, char* y) {

    return (strcmp(x, y)>0) ? x : y;

    }

    Осуществляется следующая последовательность этапов выбора

    «экземпляра» перегруженных функций:

    1. поиск специализированной (не шаблон) функции с совпадающим списком параметров, и в случае неудачи:

        • поиск шаблона функции с точным соответствием списка параметров;

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

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

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

    #include #include

    template T> T max(T x, T y) {

    return (x>y)? x : y;

    }

    char* max(char* x, char* y) {

    return (strcmp(x, y)>0) ? x : y;

    }

    void main()

    {

    int a=7, b=5;

    char c1=’a’, c2=’z’;

    char s1[]=”one”, s2[]=”two”;

    printf(“\n a=%d, b=%d, max=%d \n”, a, b, max(a, b)); printf(“\n c1=%c, c2=%c, max=%c \n”, c1, c2, max(c1, c2)); printf(“\n s1=%s, s2=%s, max=%s \n”, s1, s2, max(s1, s2));

    }

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

    Рассмотрим пример нахождения максимального значения в массивах с данными различных типов.

    . . .

    template T> Maxa(T *z, int n); // прототип шаблона

    void main(void) {

    int a[100], n;

    printf("\n Input n < 100 : "); scanf("%d", &n);

    for( int i=0; i a[%d] = ", i+1);

    scanf("%d", &a[i]);

    }

    printf("\n Max int = %d ", Maxa(a, n));

    // Вызывается функция Maxaдля типа int

    double b[ ] = {0.22, 1.5, 0.88};

    printf("\n Max double = %lf ", Maxa(b,3));

    // Вызывается функция Maxaдля типа double

    char Str[ ] = "ABC123abc";

    printf("\n Max char = %c ", Maxa(Str, (int)strlen(Str)));

    // Вызывается Maxaдля типа char

    }

    // Код шаблона

    template T>

    T Maxa(T *z, int n) { int imax = 0;

    for(int i=1; i i++)

    if(z[i]>z[imax]) imax = i;

    return (z[imax]);

    }

    Советыпопрограммированию


    При выполнении вариантов заданий придерживайтесь следующих ключевых моментов.

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

      2. Вызов шаблона это создание шаблоном конкретного варианта функции.

      3. Шаблоны можно перегружать как обычными функциями, так и другими шаблонами.

      4. Перегрузкой функций называется использование нескольких функций с одним именем и различными типами параметров.

      5. Перегрузка применяется, когда одно и то же по смыслу действие реализуется по-разному для различных типов или структур данных.

      6. При написании перегруженных функций необходимо, чтобы в процессе поиска нужного варианта функции по ее вызову не возникало неоднозначности. Неоднозначность может возникнуть из-за преобразований типов, параметров по умолчанию и ссылок.

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

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

      9. Функции не могут быть перегружены, если описание их параметров отличается только модификатором const или использованием ссылки.

      10. Многоточие в заголовке функции используется тогда, когда число аргументов неизвестно.

      11. Многоточие в заголовке функции требует использования макрокоманд для доступа к параметрам таких функций.

      12. Нарушать указанный порядок макрокоманд нельзя.



    1   ...   41   42   43   44   45   46   47   48   ...   67


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