Референца (програмирање)

Из Википедије, слободне енциклопедије

Референца је врста промјенљиве у програмирању.

Референце су најсродније показивачима. Користе се у великом броју програмских језика, као што су C++, Јава, PHP, Јаваскрипт и други.

Улога референце је да чува адресу неке друге промјенљиве и све операције које се чине на њима, просљеђују промјенљивама на које саме показују. Синтакса референци, у свим програмским језицима који их подржавају, је таква да дозвољава само рад са меморијском локацијом на коју референца указује - није дозвољен рад са меморијском локацијом која представља референцу. То можемо упоредити са показивачима, који подржавају све аритметичке операције и операције додјеле над самим показивачем, као и над вриједношћу на коју показивач показује.

У различитим програмским језицима, референце се користе другачије. У неким програмским језицима све промјенљиве су заправо референце, и не представљају саме по себи врсту промјенљиве, него указују на карактеристичан начин преноса аргумената функције (нпр. Јава, Јаваскрипт), док се у другим језицима третирају баш као посебна подврста података, попут показивача и морају се експлицитно захтијевати посебном синтаксом (нпр. C++, PHP).

C++[уреди]

У програмском језику C++, референце су уведене ради поједностављеног писања; наслиједивши комплетну синтаксу из C-а, а самим тиме и показиваче, архитекте C++-а су одлучиле увести референце које би у одређеним аспектима преузеле улогу показивача али биле и знатно једноставније за употребу.

Битно је нагласити да показивачи и референце нису еквивалентни у C++-у и да, штавише, постоје значајне разлике:

    • показивачи могу примити вриједност у било ком тренутку за вријеме рада програма
    • референце морају бити иницијализовани при конструисању, и не могу мијењати вриједност
    • показивачи могу садржати вриједност NULL, тј. не показивати нинашта
    • референце морају бити иницијализоване на неку вриједност, или ће компајлер пријавити грешку
    • показивачи се могу користити за имплементацију низова, матрица, динамичку алокацију меморије и просљеђивање аргумената функције „по референци“
    • референце се могу користити само за просљеђивање аргумената функције по референци
    • показивачи користе оператор * (звјездицу) за дереференцирање и оператор & (амперсанд) за референцирање
    • референце не користе ниједан од наведених оператора, што их чини знатно једноставнијим за рад
    • могу постојати показивачи на показиваче
    • не могу постојати показивачи на референце

Декларација[уреди]

Декларација референци у програмском језику C++ се врши на сљедећи начин:

тип_циљне_промјенљиве & име_референце = име_циљне_промјенљиве;

Из дате дефиниције видимо да се знак '&' користи за означавање да је име_референце референца, и примјећујемо да је додјела циљне промјенљиве референци неопходна већ за вријеме саме декларације референце; при навођењу циљне промјенљиве није неопходно користити никакве посебне знаке (упоредите то са додјелом показивачу, у облику: име_показивача = &име_циљне_промјенљиве, гдје је потребно навести адресни оператор (&) да би показивач примио одговарајућу вриједност).

Једном овако декларисана промјенљива се не може измијенити - свака додјела у облику:

име_референце = нека_друга_промјенљива;

ће бити еквивалентна додјели:

име_циљне_промјенљиве = нека_друга_промјенљива;

Просљеђивање аргумената[уреди]

Поглавље „декларација“ је показало школски примјер декларације референце, који се у пракси скоро никад не користи. Најчешћа употреба референци је при просљеђивању аргумената функцији, када се помоћу референци жели рећи „аргументима који се прослиједе на овом мјесту, функција ће моћи прилазити директно и мијењати њихову вриједност“. Другим ријечима, референца ће тада преузети улогу показивача, и дозволити приступ оригиналној промјенљивој која је прослијеђена у тренутку позива функције.

Погледајмо неколико примјера - нефункционалан примјер, затим функционалан примјер реализован са показивачима и на крају функционалан примјер реализован помоћу референци.

Нефункционалан примјер:

# include <iostream>
using namespace std;
 
void povecajZa1(int x )
{
    x = x + 1; // очекујемо да се прослијеђени аргумент
               // измијени након завршетка функције
               // оно што се заправо деси је да се прослиједи
               // само '''копија''' прослијеђеног аргумента,
               // и оригинална промјенљива остаје нетакнута
}
 
int main()
{
    int x = 3;
    povezajZa1(x );
    cout << x << endl; // штампа се 3!
}

Функционалан примјер реализован помоћу показивача:

# include <iostream>
using namespace std;
 
void povecajZa1(int * x ) // функцији ће бити прослијеђена '''адреса'''
                           // неког цијелог броја, а x ће бити показивач
                           // на локацију са том адресом
{
    *x = *x + 1; // сада се повећава оно на шта показивач x показује
                 // (а то је промјенљива чија адреса нам је послата)
}
 
int main()
{
    int x = 3;
    povezajZa1(&x ); // шаљемо адресу промјенљиве x
    cout << x << endl; // штампа се 4!
}

Функционалан примјер реализован помоћу референци:

# include <iostream>
using namespace std;
 
void povecajZa1(int & x ) // проглашавамо x '''референцом'''
                           // на прослијеђени аргумент
{
    x = x+1; // сада се повећава оно на шта референца x показује
             // (а то је '''оригинални''' прослијеђени аргумент)
}
 
int main()
{
    int x = 3;
    povezajZa1(x );
    cout << x << endl; // штампа се 4!
}

Лако је примијетити да је разлика између нефункционалног примјера и примјера са референцама много мања од разлике између нефункционалног примјера и примјера са показивачима. Показивачи захтијевају дереференцирање помоћу оператора звјездице, референцирање помоћу адресног оператора, и декларацију функције која указује на употребу показивача, док референце захтијевају само то - амперсанд у декларацији функције - и функција остаје идентична, као и њен позив.

PHP[уреди]

PHP има сложену употребу референци, чија синтакса зависи од врсте примјене. Постоји неколико примјена референци:

  • враћање референце на одређену промјенљиву као излазне вриједности функције
  • додјела једне промјенљиве другој, чиме друга постаје референца на прву, без стварног копирања вриједности
  • просљеђивање аргумента функције „по референци“ (као у језику C++
  • употреба у петљи foreach

Враћање референце[уреди]

Враћање референце као излазне вриједности функције се чини тако што ставимо амперсанд иза кључне ријечи function при декларацији функције:

function & vratiLokalnuVrijednost()
{
    $niz = Array("prvi" => 10, "drugi" => 20 );
    return $niz;
}

На тај начин функција алоцира локалну промјенљиву, али да би при враћању резултата избјегла непотребно копирање вриједности, враћа референцу на локалну промјенљиву $niz.

Референтна додјела[уреди]

Референцирање једне промјенљиве ка другој се такође постиже помоћу оператора амперсанда, али на следећи начин:

$b = 2;
$a = &$b;

Тада $a постаје дословно исто што и $b, синоним, лажно име, односно баш референца на $b. Свака промјена $a утиче и на $b и обрнуто.

Аргументи функције[уреди]

Просљеђивање аргумента функцији се врши на исти начин као и у C++-у, дакле стављајући амперсанд испред имена аргумента функције:

function povecajZa1(& $x )
{
    $x = $x+1;
}

foreach[уреди]

При кориштењу foreach петље, у сваком кругу се одговарајући елемент низа копира у привремену меморијску локацију. Уколико користимо референце, овај процес се знатно убрзава јер се ради са референцама на одговарајуће елементе низа, а не са њиховим копијама. Референце се у овој ситуацији користе тако што се стави амперсанд испред имена промјенљиве која представља име сваког појединачног елемента низа у одговарајућем кругу:

foreach ($niz as &$element ) {
    $element += 1; // ова промјена ће утицати на стварне елементе низа
}
 
foreach ($niz as $element ) {
    $element += 1; // ова промјена '''неће''' утицати на стварне елементе низа
}

Јава и Јаваскрипт[уреди]

У програмским језицима Јава и Јаваскрипт, све промјенљиве су заправо референце које указују на меморију алоцирану, посредно или непосредно, помоћу кључне ријечи new, са изузетком промјенљивих које садрже вриједности неког од елементарних типова (цјелобројне промјенљиве, знаковне вриједности, реални бројеви итд.). Изузетка, међутим, нема кад се говори о преношењу аргумената функцији; у том случају све, па и промјенљиве неког од елементарних типова, се преносе као референце. Ово представља знатно убрзање рада програма написаних у неком од ових програмских језика. Писање самих програма није постало ништа сложеније, будући да такве „референтне“ промјенљиве не захтијевају никакву посебну синтаксу и користе се као обичне статичке промјенљиве.

Види још[уреди]