Теоретичні відомості

2.1. Функції з початковими значеннями параметрів (по-замовчуванню)

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

const int N = 20;/ / кількість елементів масиву

char mas1 [N] [10];/ / масив імен

int mas2 [N] / / масив віків

void init (int i, char * name = "Вася", int age = 17)

{

strcpy (mas1 [i], name);

mas2 [i] = age;

}

Приклади використання функції init ():

1. for (int i = 1; i <N; i + +)

init (i);

Всім елементам масиву mas1 присвоюється значення «Вася», всім елементам масиву mas2 присвоюється значення 17.

2. for (int i = 1; i <N; i + +)

{

char Name [10];

cout << "Введіть ім'я:"; cin >> Name;

init (I, Name);}

Всім елементам масиву mas1 присвоюється значення змінної Name, всім елементам масиву mas2 присвоюється значення 17.

2.2. Функції зі змінним числом параметрів

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

тип ім'я (явні параметри,...)

{тіло функції}

Після списку обов'язкових параметрів ставиться кома, а потім три крапки, яке показує, що подальший контроль відповідності кількості і типів параметрів при обробці виклику функції виробляти не потрібно. При зверненні до функції всі параметри і обов'язкові, і необов'язкові будуть розміщуватися в пам'яті один за одним. Отже, визначивши адресу обов'язкового параметра як p = & k, де p - вказівник, а k - обов'язковий параметр, можна отримати адреси і всіх інших параметрів: оператор k + +; виконує перехід до наступного параметра списку. Ще одна складність полягає у визначенні кінця списку параметрів, тому кожна функція зі змінним числом параметрів повинна мати механізм визначення кількості і типів параметрів. Існує два підходи:

1) відомо кількість параметрів, що передається як обов'язковий параметр;

2) відомий ознака кінця списку параметрів.

/ / Знайти середнє арифметичне послідовності

/ / Чисел, якщо відомо кількість чисел

# Include <iostream.h>

float sum (int k,...)

/ / Явний параметр k задає кількість чисел

{

int * p = &k; / / набудували покажчик на параметр k

int s = 0;

for (; k! = 0; k -)

s + = * (+ + p);

return s / k;

}

void main ()

{

/ / Середнє арифметичне 4 +6

cout << "\ n4 +6 =" << sum (2,4,6);

/ / Середнє арифметичне 1 +2 +3 +4 (добре)

cout << "\ n1 +2 + +3 +4 =" << sum (4,1,2,3,4);

}

Для доступу до списку параметрів використовується покажчик * p типу int. Він встановлюється на початок списку параметрів в пам'яті, а потім переміщається по адресам фактичних параметрів (+ + p).

/ * Знайти середнє арифметичне послідовності чисел, якщо відомий ознака кінця списку параметрів * /

# Include <iostream.h>

int sum (int k,...)

{

int * p = &k; / / набудували покажчик на параметр k

int s = * p; / / значення першого параметра привласнили s

for (int i = 1; p! = 0; i + +) / / поки немає кінця списку

s + = * (+ + p);

return s / (i-1);

}

void main ()

{

/ / Знаходить середнє арифметичне 4 +6

cout << "\ n4 +6 =" << sum (4,6,0);

/ / Знаходить середнє арифметичне 1 +2 +3 +4 (добре)

cout << "\ n1 +2 + +3 +4 =" << sum (1,2,3,4,0);

}

2.3. Перевантаження функцій

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

#іnclude <iostream.h>

#include <string.h>

/ / Порівняння двох цілих чисел

int max (int a, int b)

{

if (a> b) return a;

else return b;

}

/ / Порівняння двох дійсних чисел

float max (float a, float b)

{

if (a> b) return a;

else return b;

}

/ / Порівняння двох рядків

char * max (char * a, char * b)

{

if (strcmp (a, b)> 0) return a;

else return b;

}

void main ()

{

int a1, b1;

float a2, b2;

char s1 [20];

char s2 [20];

cout << "\ nfor int: \ n";

cout << "a =?"; cin >> a1;

cout << "b =?"; cin >> b1;

cout << "\ nMAX =" << max (a1, b1) << "\ n";

cout << "\ nfor float: \ n";

cout << "a =?"; cin >> a2;

cout << "b =?"; cin >> b2;

cout << "\ nMAX =" << max (a2, b2) << "\ n"

cout << "\ nfor char *: \ n";

cout << "a =?"; cin >> s1;

cout << "b =?"; cin >> s2;

cout << "\ nMAX =" << max (s1, s2) << "\ n";

}

Правила опису перевантажених функцій:

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

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

• Функції не можуть бути перевантажені, якщо опис їх параметрів відрізняється тільки модифікатором const або наявністю посилання: функції int & f1 (int &, const int &) {...} і int f1 (int, int) {...} - не є перевантаженими, т. до. компілятор не зможе дізнатися яка з функцій викликається, тому що немає синтаксичних відмінностей між викликом функції, яка передає параметр за значенням і функції, яка передає параметр по посиланню.

2.3. Шаблони функцій

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

template <class імя_тіпа [,class імя_тіпа]>

заголовок_функціі

{

тіло функції

}

Таким чином, шаблон сімейства функцій складається з 2 частин - заголовка шаблону: template <список параметрів шаблону> і звичайного визначення функції, в якому замість типу значення, що повертається і / або типу параметрів, записується ім'я типу, визначене в заголовку шаблону.

/ / Порівняння двох чисел будь-якого типу

template <class T>

T max (T a, T b)

{

if (a> b) return a;

else return b;

}

Шаблон служить для автоматичного формування конкретних описів функцій по тим викликам, які компілятор виявляє в програмі. Наприклад, якщо в програмі виклик функції здійснюється як max (1,5), то компілятор сформує визначення функції int max (int a, int b) {...}.

2.4. Покажчик на функцію

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

Покажчик на функцію визначається наступним чином:

тіп_функціі (* імя_указателя) (специфікація параметрів)

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

Виклик функції за допомогою покажчика має вигляд:

(* Імя_указателя) (список фактичних параметрів);

# іnclude <iostream.h>

int f1 (char * S)

{

cout << S << "\ n";

return 1;

}

void main ()

{

char s [20] = "\ nfunction1";

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

ptr1 = f1; / / покажчику привласнюється адреса функції f1

cout << ((* ptr1) (s)); / / виклик функції f1 c допомогою покажчика

}

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

# іnclude <iostream.h>

# іnclude <math.h>

typedef float (* fptr) (float);/ / тип-покажчик на функцію рівняння

/ * Рішення рівняння методом половинного ділення, рівняння передається за допомогою покажчика на функцію * /

float root (fptr f, float a, float b, float e)

{

float x;

do

{

x = (a + b) / 2; / / знаходимо середину відрізка

if ((* f) (a) * f (x) <0) / / вибираємо відрізок

b = x;

else a = x;

}

while ((* f) (x)> e && fabs (a-b)> e);

return x;

}

/ / Функція, для якої шукається корінь

float testf (float x)

{

return x * x-1;

}

void main ()

{

/ * В функцію root передається покажчик на функцію, координати відрізка і точність * /

float res = root (testf, 0,2,0.0001);

cout << "\ nX =" << res;

}


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: