ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Опції в С ++
         

     

    Інформатика, програмування

    Опції в С + +

    Звичайний спосіб зробити що-небудь в C + + програмі - це викликати функцію, яка це робить. Визначення функції є способом задати те, як має робитися деяку дію. Функція не може бути викликана, поки вона не описана.

    Описи Функцій

    Опис функції задає ім'я функції, тип повертається функцією значення (якщо таке є) і число і типи параметрів, які повинні бути у виклику функції.

    Наприклад:

    extern double sqrt (double);

    extern elem * next_elem ();

    extern char * strcpy (char * to, const char * from);

    extern void exit (int);

    Семантика передачі параметрів ідентична семантиці ініціалізації. Перевіряються типи параметрів, і коли потрібно проводиться неявне перетворення типу. Наприклад, якщо були задані попередні визначення, то

    double sr2 = sqrt (2);

    буде правильно звертатися до функції sqrt () зі значенням з плаваючою точкою 2.0. Значення такої перевірки типу і перетворення типу величезне.

    Опис функції може містити імена параметрів. Це може допомогти читачеві, але компілятор ці імена просто ігнорує.

    Визначення Функцій

    Кожна функція, що викликається в програмі, повинна бути десь визначена (тільки один раз). Визначення функції - це опис функції, в якому наводиться тіло функції.

    Наприклад:

    extern void swap (int *, int *);// опис

    void swap (int *, int *)// визначення

    (

    int t = * p;

    * p =* q;

    * q = t;

    )

    Щоб уникнути витрат на виклик функції, функцію можна описати як inline, а щоб забезпечити більш швидкий доступ до параметрів, їх можна описати як register. Обидва кошти можуть використовуватися неправильно, і їх слід уникати скрізь де є які-небудь сумніви в їх корисності.

    Передача Параметрів

    Коли викликається функція, додатково виділяється пам'ять під її формальні параметри, і кожен формальний параметр ініціалізується відповідним йому фактичним параметром. Семантика передачі параметрів ідентична семантиці ініціалізації. Зокрема, тип фактичного параметра зіставляється з типом формального параметра, і виконуються всі стандартні і визначені користувачем перетворення типів. Є особливі правила для передачі векторів, засіб передавати параметр без перевірки і засіб для завдання параметрів за замовчуванням .

    Розглянемо

    void f (int val, int & ref)

    (

    val ++;

    ref ++;

    )

    Коли викликається f (), val + + збільшує локальну копію першого фактичного параметра, тоді як ref + + збільшує друге фактичний параметр.

    Наприклад:

    int i = 1;

    int j = 1;

    f (i, j);

    збільшує j, але не i. Перший параметр, i, передається по значенню, другий параметр, j, передається за посиланням. Як вже зазначалося в цьому розділі, використання функцій, які змінюють передані за посиланням параметри, можуть зробити програму важко читається, і їх слід уникати. Однак передача великого об'єкта за посиланням може бути набагато ефективніше, ніж передача його за значенням. У цьому випадку параметр можна описати як const, щоб вказати, що посилання застосовується з міркувань ефективності, а також щоб не дозволити викликається функції змінювати значення об'єкту:

    void f (const large & arg)

    (

    // значення "arg" не може бути змінено

    )

    Аналогічно, опис параметра покажчика як const повідомляє читачеві, що значення об'єкта, зазначених вище покажчиком, функцією не змінюється.

    Наприклад:

    extern int strlen (const char *);// з

    extern char * strcpy (char * to, const char * from);

    extern int strcmp (const char *, const char *);

    Важливість такої практики зростає з розміром програми.

    Зауважте, що семантика передачі параметрів відмінна від семантики привласнення. Це важливо для const параметрів, посилальних параметрів і параметрів деяких типів, що визначаються користувачем.

    Повернення Значення

    З функції, яка не описана як void, можна (і треба) повертати значення. Повертає значення задається оператором return.

    Наприклад:

    int fac (int n) (return (n> 1)? n * fac (n-1): 1;)

    У функції може бути більше одного оператора return:

    int fac (int n)

    (

    if (n> 1)

    return n * fac (n-1);

    else

    return 1;

    )

    Як і семантика передачі параметрів, семантика повернення функцією значення ідентична семантиці ініціалізації. Повертає значення розглядається як ініціалізатор змінної що повертається типу. Тип що повертається вирази перевіряється на узгодженість з повертається типом і виконуються всі стандартні і визначені користувачем перетворення типів.

    Наприклад:

    double f ()

    (

    // ...

    return 1;// неявно перетвориться до double (1)

    )

    Кожного разу, коли викликається функція, створюється нова копія її параметрів і автоматичних змінних. Після повернення з функції пам'ять використовується заново, тому повертати покажчик на локальну змінну нерозумно. Зміст зазначених вище місця зміниться непередбачувано:

    int * f () (

    int local = 1;

    // ...

    return &local;// так не робіть

    )

    Ця помилка менш звичайна, ніж еквівалентна помилка при використанні посилань:

    int & f () (

    int local = 1;

    // ...

    return local;// так не робіть

    )

    На щастя, про такі повертаються значеннях попереджає компілятор.

    Ось інший приклад:

    int & f () (return 1;)// так не робіть

    Векторні Параметри

    Якщо як параметр функції використовується вектор, то передається покажчик на його перший елемент.

    Наприклад:

    int strlen (const char *);

    void f ()

    (

    char v [] = "a vector"

    strlen (v);

    strlen ( "Nicholas ");

    );

    Інакше кажучи, при передачі як параметр типу T [] перетвориться до T *. Отже, надання елементу векторного параметра змінює значення елемента вектора, який є параметром. Іншими словами, вектор відрізняється від усіх інших типів тим, що вектор не передається (і не може передаватися) за значенням.

    Розмір вектора недоступний викликається функції. Це може бути незручно, але цю складність можна обійти декількома способами. Строки закінчуються нулем, тому їх розмір можна легко знайти. Для інших векторів можна передавати другий параметр, який задає розмір, або визначити тип, що містить покажчик і індикатор довжини, і передавати його замість просто вектора.

    Наприклад:

    void compute1 (int * vec_ptr, int vec_size);// один спосіб

    struct vec (//інший спосіб

    int * ptr;

    int size;

    );

    void compute2 (vec v);

    З багатовимірними масивами все хитріше, але часто можна замість них використовувати вектори покажчиків, які не вимагають спеціального розгляду.

    Наприклад:

    char * day [] = (

    "mon", "tue", "wed", "thu", "fri", "sat", "sun"

    );

    З іншого боку, розглянемо визначення функції, яка працює з двовимірними матрицями. Якщо розмірність відома на стадії компіляції, то ніяких проблем немає:

    void print_m34 (int m [3] [4])

    (

    for (int i = 0; i <3; i + +) (

    for (int j = 0; j <4; j ++)

    cout << "" <

    cout << "n";

    )

    )

    Матриця, звичайно, все одно передається як покажчик, а розмірності використовуються просто для зручності запису.

    Перша розмірність масиву не має відношення до задачі відшукання положення елемента. Тому її можна передавати як параметр:

    void print_mi4 (int m [] [4], int dim1)

    (

    for (int i = 0; i

    Параметри за замовчуванням

    Часто в самому загальному випадку функції потрібно більше параметрів, ніж у самому простому і більш вживаною випадку. Наприклад, в бібліотеці потоків є функція hex (), що породжує рядок з шістнадцяткові поданням цілого. Другий параметр використовується для завдання числа символів для представлення першого параметра. Якщо кількість символів занадто мало для подання цілого, відбувається усікання, якщо воно занадто велике, то рядок доповнюється пробілами. Часто програміст не піклується про число символів, необхідних для представлення цілого, оскільки символів достатньо. Тому для нуля в якості другого параметра визначено значення "використовувати стільки символів, скільки потрібно ". Щоб уникнути засмічення програми викликами на зразок hex (i, 0), функція описується так:

    extern char * hex (long, int = 0);

    Ініціалізатор другого параметра є параметром по замовчуванням. Тобто, якщо у виклику дана тільки один параметр, як друга використовується параметр за замовчуванням.

    Наприклад:

    cout << "**" <

    інтерпретується як

    cout << "**" <

    та надрукує:

    ** 1f 20 **

    Параметр за замовчуванням проходить перевірку типу під час опису функції і обчислюється під час її виклику. Задавати параметр по замовчуванням можливо тільки для останніх параметрів, тому

    int f (int, int = 0, char * = 0);// ok

    int g (int = 0, int = 0, char *);// помилка

    int f (int = 0, int, char * = 0);// помилка

    Зауважте, що в цьому контексті пробіл між * і = є істотним (*= є операцією привласнення):

    int nasty (char *= 0);// синтаксична помилка

    Перевантаження Імен Функцій

    Як правило, давати різних функцій різні імена - думка хороша, але коли деякі функції виконують однакову роботу над об'єктами різних типів, може бути більш зручно дати їм одне й те саме ім'я. Використання одного імені для різних дій над різними типами називається перевантаженням (overloading). Метод вже використовується для основних операцій C + +: у складання існує тільки одне ім'я, +, але його можна застосовувати для складання значень цілих, плаваючих та вказівних типів. Ця ідея легко розширюється на обробку операцій, визначених користувачем, тобто, функцій. Щоб вберегти програміста від випадкового повторного використання імені, ім'я може використовуватися більш ніж для однієї функції тільки якщо воно спочатку описано як переобтяжене.

    Наприклад:

    overload print;

    void print (int);

    void print (char *);

    Що стосується компілятора, єдине спільне, що мають функції з однаковими іменами, це ім'я. Імовірно, вони в якомусь сенсі схожі, але в цьому мова ні обмежує програміста, ні допомагає йому. Таким чином, перевантажені імена функцій - це головним чином зручність запису. Це зручність значно в разі функцій із загальноприйнятими іменами кшталт sqrt, print і open. Коли ім'я семантично значуща, як це має місце для операцій на кшталт +, * І <<і у випадку конструкторів, це зручність стає суттєвим.

    Коли викликається перевантажена f (), компілятор повинен зрозуміти, до якої з функцій з ім'ям f слід звернутися. Це робиться шляхом порівняння типів фактичних параметрів з типами формальних параметрів всіх функцій з ім'ям f. Пошук функції, яку треба викликати, здійснюється за три окремих кроки:

    Шукати функцію відповідну точно, і використовувати її, якщо вона знайдена;

    Шукати відповідну функцію використовуючи вбудовані перетворення і використовувати будь-яку знайдену функцію; і

    Шукати відповідну функцію використовуючи перетворення, визначені користувачем, і якщо безліч перетворень єдино, використовувати знайдену функцію.

    Наприклад:

    overload print (double), print (int);

    void f ();

    (

    print (1);

    print (1.0);

    )

    Правило точної відповідності гарантує, що f надрукує 1 як ціле і 1.0 як число з плаваючою точкою. Нуль, char або short точно відповідають параметру int. Аналогічно, float точно відповідає double.

    До параметрів функцій з перевантаженими іменами стандартні C + + правила перетворення застосовуються не повністю. Перетворення, що можуть знищити інформацію, не виконуються. Залишаються int в long, int в double, нуль в long, нуль в double і перетворення покажчиків: нуль в покажчик, нуль в void *, і покажчик на похідний клас у вказівник на базовий клас.

    Ось приклад, в якому перетворення необхідно:

    overload print (double), print (long);

    void f (int a);

    (

    print (a);

    )

    Тут a може бути надрукована або як double, або як long. Неоднозначність дозволяється явним перетворенням типу (або print (long (a)) або print (double (a))).

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

    Наприклад:

    overload pow;

    int pow (int, int);

    double pow (double, double);// з

    complex pow (double, complex);// з

    complex pow (complex, int);

    complex pow (complex, double);

    complex pow (complex, complex);

    Процес пошуку підходящої функції ігнорує unsigned і const.

    незаданій Число Параметрів

    Для деяких функцій неможливо поставити число і тип всіх параметрів, які можна очікувати у виклику. Таку функцію описують завершуючи список описів параметрів трьома крапками (...), що означає "і може бути, ще якісь параметри ".

    Наприклад:

    int printf (char * ...);

    Це задає, що у виклику printf повинен бути по меншій б один параметр, char *, а інші можуть бути, а можуть і не бути.

    Наприклад:

    printf ( "Hello, worldn ");

    printf ( "Моє ім'я% s% sn", first_name, second_name);

    printf ( "% d +% d =% dn", 2,3,5);

    Така функція покладається на інформацію, яка недоступна компілятору при інтерпретації її списку параметрів. У разі printf () першим параметром є рядок формату, що містить спеціальні послідовності символів, що дозволяють printf () правильно обробляти інші параметри. % s означає "чекай параметра char *", а% d означає "жди параметра int". Однак, компілятор цього не знає, тому він не може переконатися в тому, що очікувані параметри мають відповідний тип.

    Наприклад:

    printf ( "Моє ім'я% s% sn", 2);

    відкомпілює і в кращому випадку призведе до якої-небудь дивного вигляду видачі.

    Очевидно, якщо параметр не був описаний, то у компілятора немає інформації, необхідної для виконання над ним перевірки типу і перетворення типу. У цьому випадку char або short передаються як int, а float передається як double. Це не обов'язково те, чого чекає користувач.

    Надмірне використання крапок, на зразок wild (...), цілком вимкнути перевірку типів параметрів, залишаючи програміста відкритим перед безліччю неприємностей, які добре знайомі програмістам на C. У добре продуманій програмі потрібно щонайбільше кілька функцій, для яких типи параметрів не визначені повністю. Для того, щоб подбати про перевірки типів, можна використовувати перевантажені функції та функції з параметрами за замовчуванням у більшості тих випадків, коли інакше довелося б залишити типи параметрів незаданій. Многоточие необхідно тільки якщо змінюються і число параметрів, і тип установки. Найбільш звичайне застосування багатокрапки в завданні інтерфейсу з функціями C бібліотек, які були визначені в той час, коли альтернативи не було:

    extern int fprintf (FILE *, char * ...);// з

    extern int execl (char * ...);// з

    extern int abort (...);// з

    Розберемо випадок написання функції помилок, яка отримує один цілий параметр, який вказує серйозність помилки, після якого йде довільну кількість рядків. Ідея полягає в тому, щоб складати повідомлення про помилку за допомогою передачі кожного слова як окремого строкового параметра:

    void error (int ...);

    main (int argc, char * argv [])

    (

    switch (argc) (

    case 1:

    error (0, argv [0], 0);

    break;

    case 2:

    error (0, argv [0], argv [1], 0);

    default:

    error (1, argv [0], "с", dec (argc-1), "параметрами", 0);

    )

    )

    Функцію помилок можна визначити так:

    # include

    void error (int n ...)

    /*

    "n" з наступним списком char *, що закінчуються нулем

    */

    (

    va_list ap;

    va_start (ap, n);// розкрутка arg

    for (;;) (

    char * p = va_arg (ap, char *);

    if (p == 0) break;

    cerr <

    )

    va_end (ap);// очищення arg

    cerr << "n";

    if (n) exit (n);

    )

    Перший з va_list визначається і ініціалізується викликом va_start (). Макрос va_start отримує ім'я va_list "а й ім'я останнього формального параметра як параметри. Макрос va_arg використовується для вибору неіменованних параметрів по порядку. При кожному зверненні програміст повинен задати тип; va_arg () припускає, що був переданий фактичний параметр, але звичайно способу переконатися в цьому немає. Перед поверненням з функції, в якій був використаний va_start (), повинен бути викликаний va_end (). Причина в тому, що va_start () може змінити стек так, що не можна буде успішно здійснити повернення; va_end () анулює всі ці зміни.

    Покажчик на Функцію

    З функцією можна робити тільки дві речі: викликати її і брати її адресу. Покажчик, отриманий взяттям адреси функції, можна потім використовувати для виклику цієї функції.

    Наприклад:

    void error (char * p) (/ * ... * /)

    void (* efct) (char *);// покажчик на функцію

    void f ()

    (

    efct = &error;// efct вказує на error

    (* efct) ( "error");// виклик error через efct

    )

    Щоб викликати функцію через вказівник, наприклад, efct, треба спочатку цей покажчик разименовать, * efct. Оскільки операція виклику функції () має більш високий пріоритет, ніж операція разименованія *, то не можна писати просто * efct ( "error"). Це означає * efct ( "error"), а це помилка в типі. Те ж відноситься і до синтаксису описів.

    Зауважте, що у покажчиків на функції типи параметрів описуються точно також, як і в самих функціях. У привласнення покажчика повинна дотримуватися точну відповідність повного типу функції.

    Наприклад:

    void (* pf)(char *);// покажчик на void (char *)

    void f1 (char *);// void (char *)

    int f2 (char *);// int (char *)

    void f3 (int *);// void (int *)

    void f ()

    (

    pf = &f1;// ok

    pf = &f2;// помилка: не підходить повертається тип

    pf = &f3;// помилка: не підходить тип параметра

    (* pf) ( "asdf");// ok

    (* pf) (1);// помилка: не підходить тип параметра

    int i = (* pf) ( "qwer");// помилка: void присвоюється int "у

    )

    Правила передачі параметрів для безпосередніх викликів функції і для викликів функції через покажчик одні й ті ж.

    Часто, щоб уникнути використання будь-якого неочевидного синтаксису, буває зручно визначити ім'я типу покажчик-на-функцію.

    Наприклад:

    typedef int (* SIG_TYP) ();// з

    typedef void (* SIG_ARG_TYP);

    SIG_TYP signal (int, SIG_ARG_TYP);

    Буває часто корисний вектор вказівників на функцію. Наприклад, система меню для мого редактора з мишею * 4 реалізована за допомогою векторів покажчиків на функції для представлення дій. Детально цю систему тут описати не вийде, але загальна ідея:

    typedef void (* PF )();

    PF edit_ops [] = (//операції редагування

    cut, paste, snarf, search

    );

    PF file_ops [] = (//керування файлом

    open, reshape, close, write

    );

    Потім визначаємо і ініціалізіруем покажчики, що визначають дії, обрані в меню, яке пов'язане з кнопками (button) миші:

    PF * button2 = edit_ops;

    PF * button3 = file_ops;

    У повній реалізації для визначення кожного пункту меню потрібно більше інформації. Наприклад, десь повинна зберігатися рядок, що задає текст, який висвічується. При використанні системи значення кнопок миші часто міняється залежно від ситуації. Ці зміни здійснюються (частково) за допомогою зміни значень покажчиків кнопок. Коли користувач обирає пункт меню, наприклад пункт 3 для кнопки 2, виконується пов'язане з ним дію:

    (button2 [3 ])();

    Один зі способів оцінити величезну міць покажчиків на функції - це спробувати написати таку систему не використовуючи їх. Меню можна міняти в ході використання програми, вносячи нові функції в таблицю дій. Під час виконання можна також легко сконструювати нове меню.

    Покажчики на функції можна використовувати для завдання поліморфних підпрограм, тобто підпрограм, які можуть застосовуватися до об'єктам багатьох різних типів:

    typedef int (* CFT) (char *, char *);

    int sort (char * base, unsigned n, int sz, CFT cmp)

    /*

    сортує "n" елементів вектора "base"

    у зростаючому порядку

    за допомогою функції порівняння, що вказується "cmp".

    Розмір елементів "sz".

    Дуже неефективний алгоритм: бульбашкова сортування

    */

    (

    for (int i = 0; iname, Puser (q) -> name);

    )

    int cmp2 (char * p, char * q)// Порівнює числа dept

    (

    return Puser (p) -> dept-Puser (q) -> dept;

    )

    Ця програма сортує і друкує:

    main ()

    (

    sort ((char *) heads, 6, sizeof (user), cmp1);

    print_id (heads, 6);// в алфавітному порядку

    cout << "n";

    sort ((char *) heads, 6, sizeof (user), cmp2);

    print_id (heads, 6);// по порядку підрозділів

    )

    Можна взяти адреса inline-функції, як, втім, і адреса перевантаженій функції.

    Список літератури

    Для підготовки даної роботи були використані матеріали з сайту http://www.realcoding.net

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status