Помогни ни да направим Uroci.net по - богат! Добави урок

C++ част.4 (Функции и обхват)

C++ » C++
fix3d   трудност:    видян: 42358



4.5. Списък от аргументи на функция

Различните функции в една програма могат да комуникират по два начина. (Под комуникация се разбира разделен достъп до стойности). При първият начин се използват глобални променливи, а при втория списък от формални аргументи на функция.

Общата достъпност на глобалните променливи за цялата програма носи голяма полза, но изисква и особено внимание. Видимостта на глобалните променливи ги прави удобен метод за комуникация между различните части на програмата. Съществуват няколко пречки за да разчитаме само на глобалните променливи при комуникацията между функциите:

- функциите, които използват глобалните променливи, зависят от съществуването и типа им, което прави тези функции трудно използваеми в друг контекст.
- ако програмата трябва да бъде модифицирана глобалните променливи увеличават вероятността от възникване на грешки. Освен това извършването на локални промени изисква разбирането на цялата програма.
- ако една глобална променлива получи неправилна стойност трябва да бъде прегледана цялата програма за да бъде открита причината;
- няма никаква локализация на грешките.
- много трудно се пишат правилни рекурсивни програми когато функциите използват глобални променливи.

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

Пропускането на аргумент или изпращането на аргумент от неправилен тип са източници на сериозни грешки по време на изпълнение на програма, написана на предишния ANSI C език. Със въвеждането на строгата проверка на типовете, тези интерфейсни грешки почти винаги се откриват по време на компилация. Вероятността за възникване на грешка при изпращане на аргументите се увеличава с увеличаване на размера на списъка от аргументи - някои функции на FORTRAN приемат до 32 аргумента. Като едно общо правило може да се приеме, че броят на аргументите не трябва да бъде повече от осем. Ако една функция се нуждае от повече аргументи то вероятно тя се опитва да направи прекалено много неща; един по-добър проект би могъл да я раздели на две или повече по-специализирани функции.

Като една алтернатива на големия списък от аргументи програмистът може да дефинира класов тип, който да съдържа стойностите на аргументите. Полезността на това се изразява в следното:

1. Значително намалява сложността на списъка от аргументи.
2. Проверките за валидност на стойностите за аргументите могат да бъдат изпълнявани от членовете-функции на класа, а не вътре във функцията. Това намалява размера на функцията и я прави по-лесна за разбиране.

Синтаксис на списъка от аргументи

Списъкът от аргументи на функция не може да бъде пропускан. Функция, която не получава аргументи може да се опише или с празен списък от аргументи или със списък, съдържащ единствено ключовата дума void. Например, следните две декларации на fork() са еквивалентни

// equivalent declarations
int fork();
int fork( void );

Списъкът от аргументи се нарича сигнатура на функцията, защото често се използува за разграничаване на един представител на функцията от друг. Името и сигнатурата на една функция я идентифицират по уникален начин (Раздел 5.3, който се отнася за презарежането на функции обсъжда тази идея по-подробно).

Сигнатурата се състои от разделени със запетая типове на аргументи. След всеки типов спецификатор може опционно да бъде записано име. Неправилно е съкратеното записване на разделените със запетая типови декларации във сигнатурата. Например,

min( int v1, v2 ); // error
min( int v1, int v2 ); // ok

В сигнатурата не могат да фигурират два аргумента с едни и същи имена. Имената позволяват на аргументите да бъдат достъпни във тялото на функцията. Следователно имената на аргументите не са необходими за декларацията на функцията. Ако са записани, би следвало да служат за подпомагане на документирането. Например,

print( int *array, int size );

Не съществуват езиково наложени ограниченя за задаване на различни имена в списъка от аргументи в дефиницията и декларацията на една функция. Обаче, това може да обърка четящия програмата.

Специалната сигнатура многоточие ...

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

Многоточието преустановява проверката на типовете. Наличието му указва на компилатора, че могат да следват нула или повече аргументи и типовете им са неизвестни. Съществуват следните две форми на запис:

foo( arg_list, ... );
foo( ... );

При първата форма, запетаята, която следва списъка от аргументи е опционална.

Функцията printf() от стандартната изходна библиотека на С е пример за това, кога е необходимо многоточието. printf() винаги получава символен низ като първи аргумент. Дали тя ще получи и други аргументи се определя от първият й аргумент, наречен форматиращ низ. Метасимволите, зададени чрез %, показват че съществуват и допълнителни аргументи. Например,

printf( "hello, worldn" );

получава като аргумент единствен низ. Обаче,

printf( "hello, %sn", userName );

получава два аргумента. Символът % показва, че съществува и втори аргумент, а s показва, че типа на аргумента е низ. printf() е декларирана в С++ по следния начин

printf( const char* ... );

Според това описание при всяко извикване на printf() трябва да бъде изпратен един аргумент от тип char*. След това могат да бадат подавани каквито и да е аргументи.Следните две декларации не са еквивалентни

void f();
void f( ... );

В първият случай f() е декларирана като функция, която няма аргументи;
във втория - като функция с нула или повече аргументи. Обръщенията

f( someValue );
f( cnt, a, b, c );

са правилни само за втората декларация. Обръщението f(); е правилно и за двете функции.
Специалната сигнатура инициализация по подразбиране

Стойността по подразбиране е стойност, която въпреки че не е универсално приложима, може да се окаже подходяща за мнозинството от случаите. Подразбиращите се стойности ни освобождават от грижата за някакви малки детаили.

За потребителя на операционната система UNIX, например, всеки текстов файл, създаден от него, се дефинира по подразбиране с разрешение за четене и запис за него и само за четене - за останалите. Ако ние желаем да разширим или стесним правата на достъп до файла, като системата UNIX поддържа прост механизъм за модифициране или заменяне на подразбиращите се стойности. Дадена функция може да определя подразбиращи се стойности за един или повече от аргументите си използувайки синтаксиса на сигнатурата. Функция, която създава и инициализира двумерен символен масив за да симулира екран на терминал може да зададе стойности по подразбиране за височината, ширината и вида на основата за символите на екрана

char * screenInit( int height = 42, int width = 80,
char background = ‘ ‘);

Функция, която предлага стойности по подразбиране за аргументите си може да бъде викана със или без съответните фактически аргументи. Ако е подаден аргумент той припокрива стойността по подразбиране; иначе се използува подразбиращата се стойност. Правилно е всяко от следните обръщения към screnInit()

char *cursor;
// equivalent to screenInit(24, 80,’cursor = screenInit()‘);
// equivalent to screenInit(66, 80,’ cursor = screenInit( 66 )‘);
// equivalent to screenInit(66, 256,’ cursor = screenInit( 66, 256)‘);
cursor = screenInit( 66, 256, ‘#’);

Забележете, че не е възможно да зададете стойност на background без да определите height и width. Такова свързване на аргументите се нарича позиционно. Част от работата по проектирането на една функция се състои в това да бъдат подредени аргументите в сигнатурата така, че стойността, която е най-вероятно да бъде инициализирана от потребителя да се намира на първо място. Допускането при проектирането на screenInit() (достигнато вероятно на основата на експерименти) е, че height е тази стойност, която най-вероятно ще бъде задавана от потребителя.

Една функция може да дефинира подразбиращи се инициализатори за всичките си аргументи или за някакво подмножество от тях. Най-десният инициализиран аргумент трябва да бъде снабден с подразбиращ се инициализатор преди който и да е аргумент от ляво на него да бъде снабден с такъв. Отново това се дължи на позиционното свързване на аргументите при обръщението към функция. Няма ограничение инициализаторът на аргумент по подразбиране да бъде константен израз. Инициализатор на аргумент по подразбиране може да се дефинира само веднъж в даден файл. Например, неправилен е следния запис

ff( int = 0 ); // in ff.h
#include "ff.h";
ff( int i = 0 ); { ... } // error

Съществува съглашение, че подразбиращият се инициализатор се определя в декларацията на функцията, помествана в публичния заглавен файл, а не в дефиницията на функцията.

Успешната декларация на една функция може да определи допълнителни подразбиращи се инициализатори - това е един полезен метод за пригодяване на една обща функция към специфично приложение. Функцията chmod() от системната UNIX библиотека променя защитата на даден файл. Прототипът на функцията се намира в системния заглавен файл stdlib.h. Той е деклариран по следния начин

chmod( char *filePath, int protMode );

където protMode определя режима на защита на файл, а filePath представя името и пътя до местоположението на файла. Някакво частно приложение винаги променя режима на защита на файловете си на read-only. За да не се указва това всеки път chmod() се декларира повторно за да поддържа стойност по подразбиране

#include <stdlib.h>
chmod( char *filePath, int protMode = 0444 );

Даден е следния прототип на функции, деклариран в заглавен файл

ff( int a, int b = 0, int c ); // ff.h

Как можем да декларираме отново ff() в някакъв наш файл, така че b да има подразбираш се инициализатор? Написаното по-долу е правилното представя подразбираш се инициализатор

#include "ff.h"

ff( int a, int b = 0, int c); // ok

За тази повторна декларация на ff() b е най-десният аргумент без подразбиращ се инициализатор. Следователно, правилото, че инициализаторът на стойност по подразбиране се присвоява позиционно, започвайки от най-десния аргумент, не е нарушено. Фактически, сега бихме могли да дефинираме ff() за трети път

#include "ff.h"
ff( int a, int b = 0, int c);
// ok
ff( int a = 0, int b, int c);
// ok



Страници: «2 3 4 5 6 7 8 »

Сподели урока:



Регистрирайте се, за да добавите коментар


Калдейта Ком ЕООД - © 2003-. Всички права запазени.
Препоръчваме: IT Новини