4.7. Аргумент - псевдоним
(reference)
Този аргумент изпраща на функцията стойността за запис на
фактическия аргумент. Използуването му има следните ефекти:
1. Промените
на аргументите, направени във функцията, се извършват над фактическите
аргументи, а не над локалните копия.
2. Не съществуват ограничения за
изпращането на големи класови обекти като аргументи на функция. Когато се
използува механизма за изпращане по стойност се копира целия обект при всяко
обръщение към функцията.
Ако аргументът от псевдонимен тип не се променя
във функцията се препоръчва да бъде деклариран като const. Забележете, обаче, че
е неправилно декларирането на х като const в следния пример
class
X;
int foo( X& );
int bar( const X& x ) {
// const passed to
nonconst reference
return foo ( x ); // error }
x не може да бъде
изпратен като аргумент на foo() освен ако сигнатурата на foo() не бъде променена
на const X& или X. Псевдонимен аргумент от компактен тип или от тип с
плаваща запетая може да се държи неочаквано, когато фактическият аргумент не му
съответствува точно по тип. Това се дължи на факта, че се генерира временен
обект, на който се присвоява стойността за четене на фактическия аргумент, и
тогава този обект се изпраща на функцията. Например, ето какво ще се случи,
когато извикате rswap() с аргумент unsigned int
int i = 10;
unsigned
int ui = 20;
rswap( i, ui );
Това обръщение се интерпретира
така
int T2 = int(ui);
rswap( i, T2 );
Изпълнението на това
обръщение към rswap() дава следния неправилен резултат
Before swap()
i
10 j 20
After swap()
i 20 j 20
ui остава непроменена, понеже тя
никога не се изпраща на rswap(). По-скоро се изпраща T2 - временно генерирания
обект поради несъответствието на типовете. В резултат се моделира изпращането по
стойност. (Компилаторът трябва да издаде
предупреждение).
Аргументът-псевдоним е особено подходящ за използуване
при дефинирани от потребителя класови типове, когато можем да бъдем сигурни в
точното съответствие на типовете и е известен размера на обекта. Предварително
дефинираните типове данни не работят така добре с псевдонимната семантика. Ако
броим и unsigned съществуват осем различни цели типа като при седем от тях
винаги ще се получава несъответствие на произволен компактен псевдонимен
аргумент. В случаите, когато не можем да предвидим типа на фактическият аргумент
не е безопасно да разчитаме на семантиката на изпращане чрез псевдоним.
Аргументът-указател позволява модифицирането на обекта, който адресира. Как
бихме могли, обаче, да променяме самия указател? Тогава ще декларираме
указател-псевдоним
void prswap( int *&v1, int *v2 ) {
int *tmp =
v2;
v2 = v1;
v1 = tmp; }
Декларцията int *&p1; трябва да бъде
четена от ляво на дясно. p1 е псевдоним на указател към обект от тип int.
Променената реализация на main() ще изглежда така
#include
<stream.h>
void prswap( int *v1, int *&v2 );
main() {
int
i = 10;
int j = 20;
int *pi = &i;
int *pj = &j;
cout
<< "Before swap()tpi "<< *pi << "tpj" << *pj <<
"n";
prswap( pi, pj );
cout << "After swap()tpi "<< *pi
<< "tpj" << *pj << "n";
}
Когото компилираме и
изпълним тази програма ще получим следния резултат
Before swap()
i 10
j 20
After swap()
i 20 j 10
По подразбиране връщаният тип също
се изпраща по стойност. За големи класови обекти псевдонимният или указателният
тип за връщане е по-ефективен; самият обект не се копира.
Един втори
начин за използуване на псевдонимния тип позволява дадена функция да бъде
направена обект на присвояване. Фактически, псевдонимът връща стойността за
запис на обекта, който сочи. Това е особено важно при функции като индексния
оператор за класа IntArray, които трябва да осигуряват възможност както за
четене, така и за запис
int IntArray
operator[]( int index ) {
return ia[ index ]; }
Intarray myAarray[ 8 ];
myArray[ 2 ] = myArray[
1 ] + myArray[ 0 ];
Раздел 2.8 съдържа дефиницията на класа
IntArray.