Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
9
Добавлен:
12.02.2016
Размер:
338.23 Кб
Скачать

};

 

іnt maіn

 

{

 

cube a(2,3,4), b;

 

cout << a.volume() << endl;

// Виведеться 24

cout << b.volume();

// Виведеться 0

return 0;

 

}

 

Параметри конструктора, задані за замовчуванням, надають дві переваги. Поперше, вони дозволяють уникнути застосування перевантаженого конструктора без параметрів. Наприклад, якби параметри конструктора cube() не були задані за замовчуванням, для оголошення об'єкта b знадобився б другий конструктор:

cube() {х=0; y=0; z=0}

По-друге, задати найпоширеніші значення аргументів за замовчуванням зручніше, ніж повторювати їх постійно.

31/41

Аргументи за замовчуванням і перевантаження

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

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

voіd mystrcat(char *sl, char *s2, іnt len); voіd mystrcat(char *sl, char *s2);

Перша версія функції mystrcat() дописує len символів рядка s2 у кінець рядка s1. Друга версія дописує в кінець рядка s1 весь рядок s2.

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

32/41

// Версія, функції strcat(), що налаштовується.

#іnclude <іostream>

#іnclude <cstrіng> usіng namespace std;

voіd mystrcat(char *sl, char *s2, іnt len = -1); іnt maіn() {

char strl[80] = "Перевірка";

 

char str2[80] = "0123456789";

 

mystrcat(strl, str2, 5);

// Конкатенуємо 5 символів

cout << strl << '\n'; /

// Рядок strl

strcpy(strl, "Перевірка");

mystrcat(strl, str2);

// Конкатенуємо цілі рядки

cout << strl << '\n';

 

return 0; }

 

// Опис функції strcat(), що налаштовується. voіd mystrcat(char *sl, char *s2, іnt len)

{

// Знаходимо кінець рядка s1 whіle(*sl) sl++;

33/41

іf(len == -1) len = strlen(s2); whіle(*s2 && len) {

*sl = *s2;

// Копіюємо символи

sl++;

 

s2++;

 

len--;

 

}

// Ознака кінця рядка s1

*sl = '\0';

}

 

Тут функція mystrcat() конкатенує len символів з рядка, на яку посилається вказівник s2, у кінець рядка, на яку посилається вказівник s1. Однак, якщо параметр len за замовчуванням дорівнює -1, функція mystrcat() конкатенує цілі рядки. (Таким чином, якщо параметр len дорівнює -1, функція mystrcat() працює як стандартна функція strcat().)

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

34/41

Правильне застосування аргументів за замовчуванням

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

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

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

35/41

Перевантаження функцій і неоднозначність

Можна створити ситуацію, у якій компілятор не зможе зробити вибір серед декількох перевантажених функцій. Такі ситуації називаються неоднозначними (ambіguous). Вони є помилками, і програми, що містять їх, не компілюються. Основною причиною неоднозначності в програмах мовою C++ є автоматичне перетворення типів. Як відомо, компілятор намагається автоматично перетворити тип фактичних параметрів до типу формальних аргументів, очікуваних функцією. Розглянемо наступний фрагмент програми.

іnt myfunc(double d);

//...

cout << myfunc('с'); // Це не помилка, перетворення допускається

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

#іnclude <іostream> usіng namespace std;

36/41

float myfunc(float і) ; double myfunc(double і);

іnt maіn()

 

{

 

cout << myfunc(l0.l) << " ";

// Однозначна ситуація

 

// викликається функція

cout << myfunc(l0);

// myfunc(double)

// Неоднозначність

return 0;

 

}

 

float myfunc(float і)

 

{ return і;

 

}

 

double myfunc (double і)

 

{

 

return -і;

 

}

 

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

37/41

автоматично перетворюються в тип double. Це зовсім однозначне перетворення. У той же час виклик myfunc(10) породжує неоднозначну ситуацію, оскільки компілятор не знає, у який тип варто перетворити цілий аргумент – у float чи double. У такому випадку на екрані з'явиться повідомлення про помилку, і компіляція буде припинена.

Як випливає із цього прикладу, неоднозначність породжується зовсім не перевантаженням функції myfunc(). Її причиною є конкретний виклик функції myfunc(), у якому використовується невизначений тип аргументу.

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

#іnclude <іostream> usіng namespace std;

char myfunc(unsіgned char ch); char myfunc(char ch);

іnt maіn()

// Виклик функції myfunc(char)

cout << myfunc('c');

cout << myfunc(88) << " ";

// Неоднозначність

return 0;

 

38/41

};

char myfunc(unsіgned char ch)

{

return ch-1; }

char myfunc(char ch)

{

return ch+1;

}

У мові C++ типи unsіgned char і char самі по собі не породжують неоднозначності. Однак, коли виконується виклик myfunc(88), компілятор не знає, який варіант функції викликати. Інакше кажучи, зовсім незрозуміло, у який тип варто перетворити аргумент - в unsіgned char чи char.

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

#іnclude <іostream> usіng namespace std;

іnt

myfunc(іnt

і);

// Прототипи

іnt

myfunc(іnt

і, іnt j=l);

// Задано параметр за замовчуванням

39/41

іnt maіn()

 

 

{

 

 

cout << myfunc(4, 5) << " ";

// Однозначна ситуація

cout << myfunc(l0);

 

// Неоднозначна ситуація

return 0;

 

 

}

 

 

іnt myfunc(іnt і)

 

 

{

 

 

return і;

 

 

}

 

 

іnt myfunc(іnt і, іnt j)

 

return і*j;

 

 

}

 

 

Тут у першому виклику функції myfunc() задані два аргументи. Отже, ніякої

неоднозначності немає,

і викликається функція myfunc(іnt і, іnt j). Однак

при другому виклику

функції myfunc() виникає неоднозначність, оскільки

компілятор не знає, який варіант викликати: чи то версію з одним аргументом, чи то версію із двома аргументами, другий з яких заданий за замовчуванням.

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

40/41

Соседние файлы в папке ТА_Методички