Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Оператор const_cast



Оператор const_cast призначений для видалення атрибутів змінної const і volatile. Синтаксис використання подібний до попереднього оператора:

const_cast<type>(expr)

В кутових| дужках цільовий тип type повинен співпадати з типом виразу expr, заданим в круглих дужках.

Оператор const_cast може перетворити вказівник на об'єкт будь-якого типу до цього ж типу, але| при цьому ліквідувавши дію атрибутів const та / або volatile.

Не можна використовувати цей оператор, щоб прямо відмінити константний статус змінної.

Наприклад:

void f(const int *p)

{

int *v;

v = const_cast<int *>(p); // приведення типу позбавляє змінну,

// на яку вказує p, статусу

// константи

*v = 100; // через приведений вказівник можна

// змінити об'єкт

3 ФУНКЦІЇ. ПРОЦЕДУРНЕ ПРОГРАМУВАННЯ

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

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

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

3.1 Оголошення функцій

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

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

Базовий синтаксис оголошення прототипу функції наступний:

type function_name(type1 a1, type2 a2, typeN aN);

Тут type – це тип значення, що повертається функцією: void, int, float і т. д. Хоча як імена функцій function_name можуть використовуватися довільні імена, бажано, щоб вони відображали суть оголошуваних функцій. Функція може мати один, декілька (N) або не мати жодного аргументу. Якщо такі є, оголошення аргументів, кожне з яких включає тип і необов'язкове ім'я аргументу, повинні бути перелічені через кому в круглих дужках після імені функції. Якщо аргументи у функції відсутні, порожні дужки () все рівно повинні бути присутніми в оголошенні функції; щоб підкреслити відсутність аргументів, допускається також запис в круглих дужках слова void.

Приклади прототипів функцій

double sqrt(double);

elem* next_elem();

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

void exit(int);

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

double sr2 = sqrt(2);

містить правильний виклик функції sqrt() з аргументом з плаваючою крапкою double(2)== 2.0. Контроль і приведення типів фактичних аргументів має в мові С++ важливе значення.

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

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

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

Синтаксис визначення функції наступний:

type function_name(type1 a1, type2 a2., typeN aN)

{

// тіло функції - це

// інструкції, що реаліують алгоритм обчислення

// результату функції

return (expr); // повернення результату, якщо потрібно

}

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

Наприклад:

void swap(int*, int*); // оголошення прототипу функції

void swap(int* p, int* q) // визначення функції

{

int t = *p;

*p = *q;

*q = *t;

}

Часом бувають випадки, коли у визначенні функції не використовуються деякі аргументи:

void search(table* t, const char* key, const char*)

{

// третій аргумент не використовується

...

}

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

3.2.1 Вмонтовані (inline) функції

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

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

#include <iostream>

using namespace std;

inline long square(int а){return а * а;}

int main()

{

long sum = 0;

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

sum += square(i);

cout <<sum;

}

При компіляції, виявивши, що функціяsquare() оголошена з ключовим словом inline, компілятор замінить виклик цієї функції її реалізацією (інструкція sum += square(i); буде замінена на інструкцію sum += i * i;).

Компілятор може проігнорувати рекомендацію, задану inline, якщо, наприклад, тіло функції дуже велике або функціяє рекурсивною.

Приклад рекурсивної функції, макропідстановка якої може бути проігнорована компілятором:

inline int fac(int i) {return i<2? 1: n*fac(n-1);}

3.3 Передача аргументів при виклику функції

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

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

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

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

Щоб зрозуміти різницю, як приклад розглянемо функцію:

void f(int val, int& ref)

{

val++;

ref++;

}

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

void g()

{

int i = 1;

int j = 1;

f(i, j);

}

збільшиться значення j, але не i, оскільки перший аргумент i переданий за значенням, а другий аргумент j переданий за посиланням.

Посилання на константи часто використовуються як аргументи функцій.

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

void incr(int &aa)

{

aa++;

}

void f()

{

int x = 1;

incr(x); // x = 2

}

3.3.1 Використання посилань як аргументів

Оскільки передача аргументів має ту ж семантику, що й ініціалізація, в наведеному вище прикладі при виклику функції incr() її аргумент aa стає іншим ім'ям для x.

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

int next(int а)

{

return a+1;

}

void inc(int* p)

{

(*p)++;

}

void g()

{

int x = 1;

x = next(x); // x = 2

inc(&x); // x = 3

}

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

Як приклад запрограмуємо простий асоціативний масив. Почнемо з визначення структури pair:

struct pair

{

char* name; // рядок

int val; // ціле

};

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





Дата публикования: 2014-11-03; Прочитано: 801 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.012 с)...