Генеричко програмирање

Извор: Wikipedija
Пређи на навигацију Пређи на претрагу

У рачунарству, генеричко програмирање је техника која дозвољава да једна променљива може да чува различите типове података (такозвана вишеобличност или полиморфизам) све док су задовољени одређени услови као што су подкласа и правилна декларација. Дакле, дозвољава нам стварање функција и класа које не зависе од типа. Пример: СТЛ вектор, листа, стек итд. На пример, ако се жели направити листа користећи генеричност, могућа декларација би била Лист <Т>, где Т представља врсту података. Када се начини примерак може се направити Лист<Интегер> или Лист<Анимал>. Према листи се затим поступа као према листи оног типа података који је наведен. Од објектно оријентисаних програмских језика, програмски језици C++, D, БЕТА, Еиффел, Ада и неке верзије Јаве (1.5 и новије) подржавају генеричке типове података. ВБ.НЕТ и C# су почели да подржавају генеричке типове од верзије .НЕТ 2.0. Шаблони – основа за генеричко програмирање:  шаблон је уствари формула или рецепт за стварање класе или функције. Постоје функцијски шаблони и шаблони класе.

Генеричке функције и класе – темплате[1]

[уреди | уреди извор]

Под појмом генеричког програмирања подразумева се израда програмског кода који се у непромењеном облику може применити на различите типове података.

У C језику се за генеричко програмирање користе предпроцесорске макро наредбе, нпр:

#define Kvadrat(x) x*x

Ово је макро наредба којом се део кода који је у заградама означен са x замењује са x*x. Макро наредбе нису функције. Оне се реализују у току компајлирања лексичком заменом текста.

Пример:

Макро наредба: Извршава се као:
инт и;

Квадрат(и) * 3

инт и;

и*и*3

флоат x;

x*Квадрат(6.7)

флоат x;

x*6.7*6.7

флоат x;

x=Квадрат(x+6.7)

флоат x;

x=x+6.7*x+6.7

//грешка

У прва два примера показано је како се иста макро наредба користи за типове инт и флоат. Грешка у трећем примеру, се може избећи тако да се „аргументи“ макро наредбе напишу у заградама тј.

#define Kvadrat(x) (x)*(x)

Проблем код макроа је да је код макроа непрегледан и тешко је уочити цомпиле – грешке, будући да компајлер само код макроа налепи уместо позива и могуће су семантичке грешке које је врло тешко открити

То је један од разлога због којег у C++-у имамо генеричке (темплате) функције (функцијске шаблоне). C++ је строго типизирани језик, тј при дефиницији функције морамо навести типове параметара. C++ допушта коришћење преоптерећених (оверлоадед) функција. То су функције које имају једнака имена (и припадају истом досегу – намеспацеу) али различиту листу параметара. Преоптерећивање функције има неколико недостатака: уколико желимо нешто променити у коду функције морамо то учинити на пуно места, па се повећава и могућност грешке, не можемо предвидети на којим ће све типовима корисник хтети да позове функцију.

У C++ се исти ефекат као у C-у са #дефине може постићи коришћењем дефинисањем инлине функција:

inline int Kvadrat(int x) {
    return x*x;
}
inline float Kvadrat(float x) {
    return x*x;
}
inline double Kvadrat(double x) {
    return x*x;
}

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

T Kvadrat(T x) {
    return x*x;
}

где Т може бити инт, флоат или доубле. Ова дефиниција има генерички облик. У C++ језику се може вршити генеричко дефинисање функција, на начин да се испред дефиниције или декларације функције, помоћу кључне речи темплате, означи да Т представља „генерички тип“.

template <class T> T Kvadrat (T x) {
    return x*x;
}

или

template <typename T> T Kvadrat(T x) {
    return x*x;
}

Разлика у ова два записа је у употреби кључне речи цласс или тyпенаме, али значење је потпуно исто. Извршење позива функције се мора обавити са стварним типовима:

int x,y;
y=Kvadrat<int>(x);

Погодно је да се класе и функције могу писати генерички, параметризовано типовима података. Такве генеричке класе и функције називају се у језику C++  шаблонима (темплатес). Из шаблона се генеришу стварне класе, односно функције, за конкретне типове. Генерички механизам[2] је у потпуности статички -субституција параметара је у време превођења. Функцијски шаблон се не користи док компајлер не наиђе на позив генеричке функције. Тек тада се ствара и преводи нова варијанта функције у зависности о типу на којем је функција позвана. Тај процес стварања нове конкретне варијанте функције из функцијског шаблона назива се инстанцијација. Типови се код функцијских шаблона препознају аутоматски. Због тога се и дефиниција и имплементација таквих функција пише у .х датотеку. Компајлер тек у тренутку позива функције може одредити начин како се преводи генеричка функција, стога су генеричке функције по употреби  еквивалентне макро наредбама. Да би се могло извршити компајлирање програма у којем се користи генеричка функција, компајлеру мора бити на располагању потпуна дефиниција функције, а не само декларација функције као што је случај код употребе регуларних функција.

Генерички тип Т се може користити унутар функције за ознаку типа локалне променљиве:

template <class T> T Kvadrat(T x){
    T result=x*x;
    return result;
}
int main() {
    int i=5,k;                     
    float a=10.9,b;
    k=Kvadrat<int>(i);     
    b=Kvadrat<float>(a);
    cout <<k<<endl;
    cout<<b<<endl;
    return 0;
}
25
118.81

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

int i=5,k;
float a=10.9,b;
k=Kvadrat(i);
b=Kvadrat(a);
cout <<k<<endl;
cout<<b<<endl;

Препорука је ипак, да се увек експлицитно специфира параметарски тип. Могу се дефинисати генеричке функције са више генеричких типова. Пример:

template <class T, class U> t GetMin (T a, U b){
    return (a<b?a:b);
}

дефинише функцију са два аргумената којима се придружују генерички типови Т и У. Могуће је извршити позив функције на следећи начин: или још једноставније

i=GetMin (j,l);

Иако су ј и л различитог типа, компајлер сам врши конверзије интегралних типова.

Генерички класни типови[3]

[уреди | уреди извор]

Претпоставимо да радимо са неким скупом објеката које желимо да некако организујемо. У том случају користе се класе које се означавају као колекције (колекција=збирка, скупљање). Оне дефинишу објекте који се користе за организацију других објеката одговарајућег типа у колекције на одговарајући начин. Могуће је у колекцију додавати објекте различитог типа, али онда имамо проблем утврђивања типа објекта приликом његовог коришћења. Ако покушамо да неки објекат из ове колекције употребимо као објекат неке друге класе, наилазимо на проблем. Нпр. нека смо омогућили да колекцији могу да се додају објекти класа Тацка, Дуз и Стринг. Ако, примера ради, покушамо да употребимо неки објекат класе Дуз или Стринг као објекат класе Тацка, програм неће бити преведен. Да би се избегли овакви проблеми, треба дефинисати колекцију тако да се онемогући случајно додавање објекта погрешног типа. Један начин за решење овог проблема је дефинисати засебне колекције које могу да садрже само објекте једног типа. Али онда се јавља други проблем – имаћемо за сваки тип објекта по једну класу која дефинише одговарајућу колекцију. Други, ефикаснији начин, су управо генерички типови. Они омогућују да се дефинише само једна класа којом је колекција представљена. Сви објекти у колекцији су истог типа, али сада постоји могућност да тај тип може бити произвољан. На овај начин, имамо једну колекцију која може да се понаша и као колекција тачака, и као колекција дужи и као колекција стрингова итд. Генерички тип је класни или интерфејсни тип који има један или више параметара. Дефиниција конкретне класе или интерфејса из генеричког типа врши се додавањем конкретног аргумента за сваки од параметара генеричког типа.

public class LinkedList<T>{
    
}

Параметар Т се употребљава у дефиницији метода и поља где постоји зависност од типа овог аргумента. Појаве овог параметра у дефиницији генеричког типа се означавају као променљиве типа, јер  ће бити замењене конкретном вредношћу тог типа, на исти начин као што параметри метода бивају замењени аргументум који се проследи методу. Креирање класе из датог генеричког типа врши се навођењем одговарајућег аргумента за параметер унутар <>. Сва појављивања параметра Т у дефиницији генеричког типа биће замењена датим аргументум. Генерички тип на тај начин дефинише скуп типова, добијених за различите вредности параметра типа Т. Као аргумент за параметер Т може се проследити само класни или интерфејсни тип. Примитивни типови не могу да се користе. Уместо њих користимо одговарајуће класе које их енкапсулирају. Генерички тип ЛинкедЛист<Т> који чува доубле вредности:

LinkedList<Double> temp= new LinkedList<Double>();
temp.addItem(10.8)

Пошто је тип параметра Доубле компајлер аутоматски умеће конверзију вредности 10.8 из доубле у Доубле. Можемо да дефинишемо генерички тип који дефинише скуп класа које обухватају пар објеката произвољног типа. У том случају дефинишемо генерички тип са два параметра:

public class Par <TipKljuca, TipVrednosti> {

    public Par(TipKljuca kljuc, TipVrednosti vr) {

        this.kljuc = kljuc;
        this.vr = vr;
    }
    
    private TipKljuca kljuc;
    private TipVrednosti vr;
}

Употреба:

Par <String,String>unos =new Par<String,String>(Milan, 4445555);

Област важења параметарског типа је цела генеричка класа, искључујући статичке чланице класе. Статичка поља не могу бити дефинисана променљивом типа, нити статички методи могу да имају параметре или повратне вредности које су параметарског типа. Уколико генеричка класа садржи неко статичко поље, сваки тип произведен из датог генеричког типа, имаће своју копију статичког поља. Нека генеричка класа ЛинкедЛист<> садржи статичко поље цоунт за бројање креираних објеката. Свака класа добијена из генеричке за конкретан аргумент типа имаће своју копију овог поља. На тај начин,

цоунт у класи ЛинкедЛист<Стринг> броји креиране објекте ове класе

цоунт у класи ЛинкедЛист<Поинт> броји креиране објекте ове класе итд.

Генерички метод

[уреди | уреди извор]

Можемо да дефинишемо метод са својим независним скупом од једног или више параметара. Такав метод назива се параметарски метод или генерички метод. Генерички метод може да постоји и унутар обичне класе.

public static <T> void ispisiSve(LinkedList lista){
    for(T objekat: lista)
    System.out.println(objekat);
}

<Т> испред кога се наводи публиц или статиц кључна реч представља параметарски тип (или листу типова) за генерички метод. Овде имамо само један параметарски тип за метод исписиСве, али их може бити више. Листа параметарских типова се увек појављује између угластих заграда и наводи се иза квалификатора приступа, а пре повратне вредности метода. Аргумент типа ће бити одређен на основу параметра типа који је прослеђен при позиву метода.

Низови и параметризовани типови

[уреди | уреди извор]

Низови елемената типа добијеног од генеричког типа нису допуштени!

Колекције

[уреди | уреди извор]

Јава сyстем колекција је скуп генеричких типова које користимо за креирање колекцијских класа. Колекцијска класа је класа која организује скуп објеката датог типа на одређен начин (нпр. ланчане листе, стек,….). Највећи број ових класа које чине систем колекција дефинисане су у пакету јава.утил. Размотрићемо следеће генеричке типове:

  • Итератор<Т> интерфејсни тип - декларише методе за итерирање кроз један по један елемент колекције
  • АрраyЛист<Т> тип - има структуру динамичког низа, број објеката које можемо сачувати се аутоматски повећава по потреби.
  • ЛинкедЛист<Т> тип - двоструко повезана листа, омогућено је итерирање у оба смера

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

  • скупови
  • низови
  • каталози

Скуп(Сет) је најједноставнија колекција у којој објекти нису на неки специјалан начин уређени I објекти се једноставно додају скупу, без икакве контроле где иду. Можемо итерирати кроз све објекте скупа, испитати да ли је неки објекат члан скупа. Скуп не може да садржи дупликате. Објекат можемо и обрисати из скупа, али само ако имамо референце на њега у скупу.

Главна карактеристика низа је да су објекти смештени на линеаран начин, у произвољном, фиксном редоследу где имамо почетак и крај. Колекције у општем случају имају способност да се прошире да би се сместио потребан број објеката.Примери:

  • Низови
    • АрраyЛист, Вецтор
    • ЛинкедЛист
  • Редови
    • Стацк(ЛИФО)
    • Qуеуе(ФИФО)

Пошто је низ линеаран, могуће је додати нови објекат на почетак или крај или уметнути нови објекат иза дате позиције. Добијање објекта из низа се може извршити на висе начина:

  1. Може се селектовати први или последњи
  2. Може се добити објекат са дате позиције
  3. Може се претраживати низ унапред или уназад да би се испитало да ли се неки објекат налази у низу и слично

Итератор

[уреди | уреди извор]

Итератор је објекат који се користи за итерирање кроз колекцију, тј. приступ једном по једном објекту колекције. Употреба итератора омогуће употребу облика фор петље карактеристичног за претраживање колекција. Било који објекат који представља скуп или низ може да креира објекат типа Итератор<>. Објекат типа Итератор<> садржи референце на све објекте колекције у неком редоследу и њима се може приступити помоћу метода интерфејса Итератор<>:

  • Т неxт() -  враћа објекат типа Т и поставља Итератор<Т> објекат да при следећем позиву овог метода реферише на следећи објекат колекције. Ако нема објекта који ће бити враћен, избацује се изузетак типа НоСуцхЕлементЕxцептион
  • воид ремове() - брише последњи објекат враћен позивом метода неxт(). Ако неxт() није био позван или ако се позове ремове() два пута након позива неxт(), биће избачен изузетак типа ИллегалСтатеЕxцептион

Пример:

Класа примерак;

while(iterator.hasNext())
    primerak=iterator.next();

Овде се подразумева да је итератор типа Итератор<Класа> и да чува референце на објекат добијен из колекције. Један објекат који имплементира интерфејс Итератор<> је за једну итерацију кроз колекцију. Уколико је касније потребно поново проћи кроз све елементе колекције , неопходно је креирати нови објекат.

Лист итератори

[уреди | уреди извор]

У пакету јава.утил дефинисан је интерфејс ЛистИтератор<>. Декларише методе који се користе за кретање кроз колекцију напред и назад, тако да се до објекта може доћи висе него једном. ЛистИтератор<> интерфејс наслеђује Итератор<> интерфејс.

Списак метода:

  • Т неxт() -  као код Итератор<>
  • Боолеан хасНеxт() - као код Итератор<>
  • инт неxтИндеx() - враћа индекс објекта који ће бити враћен позивом метода неxт() као тип инт, или враћа број елемената у листи ако је *ЛистИтератор<> објекат на крају листе
  • Т превиоус() – враћа претходни објекат по редоследу у самој листи. Користимо га за враћање кроз листу.
  • Боолеан хасПревиоус() – враћа труе ако претходни позив превиоус() враћа објекат.
  • инт превиоусИндеx() - враћа индекс објекта који ће бити враћен следећим позивом метода превиоус() или -1 ако је ЛистИтератор<> објекат на почетку листе
  • воид ремове() - брише последњи објекат добијен позивом метода неxт() или превиоус(). Ако неxт() или превиоус() нису били позвани, избацује се изузетак типа ИллегалСтатеЕxцептион, а ако операција брисања није подржана за дату колекцију, избацује се изузетак типа УнсуппортедОператионЕxцептион
  • воид адд(Т обј) – додаје аргумент непосредно пре објекта који би био враћен следећем позивом метода неxт() или иза објекта који би био враћен следећим позивом метода превиоус(). Позив неxт() након додавања враћа додати елемент, а за превиоус() се ништа не мења. Ако објекат не може да се дода избавује се изузетак типа УнсуппортедОператионЕxцептион уколико постоји неки други разлог због којег додавање не може да се изврши.
  • воид сет(Т обј) – мења последњи објекат добијен позивом неxт() или превиоус(). I овај метод може да избаци изузетке типа УнсуппортедОператионЕxцептион, ЦлассЦастЕxцептион и ИллегалАргументЕxцептион.

АрраyЛист

[уреди | уреди извор]

АрраyЛист<Т> дефинише колекцију са елементима типа Т. Објекат АрраyЛист<Т> ради као и низ осим што додатно расте аутоматски, када је потребан већи капацитет. Као и низови, и АрраyЛист садрже референце на објекте, не сам објекте. То је правило за све колекције. АрраyЛист се, за разлику од низа, карактерише величином (сизе). Капацитет је максималан број објеката које АрраyЛист може да садржи. Капацитет је променљив, јер се аутоматски повећава када се дода нови објекат у већ пун вецтор. Методом енсуреЦапацитy(инт) поставља се минимални капацитет.

Пример:

v.ensureCapacity(150); /*ako je kapacitet objekta v manji od 150, kapacitet se uvećava na 150. AKo je 150 ili vise neće biti promenjen ovom naredbom.*/

Конструктори

[уреди | уреди извор]
  • АрраyЛист<>() – подразумевани конструктор креира празан АрраyЛист<> подразумеваног капацитета 10. Капацитет се повећава за 50% уколико се дода елемент у већ пун АрраyЛист.
  • АрраyЛист<Стринг> а= неw АрраyЛист<Стринг>();
  • АрраyЛист<Стринг> а= неw АрраyЛист<Стринг>(100); - експлицитно се задаје капацитет
  • сизе() – враћа број елемената који су смештени у АрраyЛист
  • адд(Т) -  додаје се објекат иза текућег последњег објекта у АрраyЛист
  • адд(инт,Т) – смешта се објекат на позицију задату првим аргументум, остали се померају за једно место у десно.
  • сет(инт,Т) – постављамо објекат у АрраyЛист на позицију задату првим аргументум, брише се објекат који је претходно био на тој позицији.
  • аддАлл(листа) – додају се сви објекти друге колекције у АрраyЛист
  • аддАлл(инт,листа)
  • гет(инт) – враћа елемент на задатој позицији

Чињеница да се капацитет АрраyЛист повећава за 50% сваки пут када се АрраyЛист попуни утиче на то да се меморија беспотребно заузима. Међутим, ово може да се превазиђе употребом метода тримТоСИзе(). Овај метод мења капацитет тако да одговара тренутној величини.

names.trimToSize(); /*postavlja  kapacitet na trenutnu veličinu. Ako je veličina u trenutku izvršavanja metoda 30 i kapacitet se postavlja na 30. Naravno i dalje se mogu dodavati novi objekti u ArrayList.*/

Можемо добити и ЛистИтератор референцу из вектора позивањем метода листИтератор().

ListIterator<String> it = names.listIterator(); /* sada je moguće kretati se u oba smera kroz ArrayList upotrebom metoda iz klase ListIterator. */
ListIterator <String>it = names.listIterator(2); /* ovim se dobija ListIterator objekat koji se odnosi samo na deo ArrayList, pri čemu je argument metoda listIterator() upravo prvi element date sekvence ArrayList. */
List <String>list = names.subList(2,5); /* dobija se podskup objekata iz ArrayList kao kolekcija tipa List<>. Argumetni metoda subList() su početak i kraj sekvence objekata koja čini podskup, pri čemu poslednji element nije uključen. */

Могуће су и комбинације:

ЛистИтератор<Стринг> листИтер = намес.субЛист(5,15).листИтератор(2); /* субЛист() враћа подлисту објеката АрраyЛист од позиције 5 до позиције 14 закључно. Након тога се за дату листу позива метод листИтератор() који враћа лист итератор над елементима листе почевши од елемента са индексом 2, па до краја. То одговара елементима полазног АрраyЛист са индексима од 7 до 14. Овим итератором се кроз дату секвенцу објеката полазног АрраyЛист може кретати у оба смера. */

Метод тоАрраy() омогућава да се елементи вектора добију у облику обичног низа.

String[] data = names.toArray(new String[names.size()]); /* argument metoda toArray() mora biti niz elemenata istog tipa kao i elementi ArrayList  ili nadtipa. Ako niz nije dovoljne veličine da primi sve elemente ArrayList, kreiraće se novi niz i referenca na taj niz će biti vraćena. Ovde metod toArray() vraća niz String[] koji sadrži sve elemente ArrayList names u odgovarajućem redosledu.*/
String[] people = {Bill, Barbara, Brian};
List<String> nameList = java.util.Arrays.asList(people);

Статички параметарски метод асЛист() дефинисан у класи јава.утил.Арраyс конвертује низ елемената датог типа Т у Лист<Т> колекцију. Аргумент метода је низ који се конвертује и враћа се референца типа Лист<Т>. Ова референца се може проследити као аргумент конструктору класе АрраyЛист:

ArrayList<String> names = new ArrayList(nameList); /* Time se dobija ArrayList koji sadrži elemente datog niza. */

Брисање елемената:

  • ремове(инт) – брисање референце на објекат на задатој позицији. Враћа се референца  на уклоњени објекат, тако да остаје могућност да се сачува референца након што се уклони из АрраyЛист.
  • ремове(Т) – брисање првог објекта са задатом референцом. Уколико је пронађен и уклоњен објекат, враћа се труе, иначе фалсе
  • ремовеАлл(намес.субЛист(2,5)); - бришу се елементи са индексима од 2 до 4
  • ретаилАлл(намес.субЛист(2,5)); - остају само елементи са индексима од 2 до 4
  • ремовеАллЕлементс() – уклања све елементе из АрраyЛист

Да ли је АрраyЛист празан може се утврдити методом исЕмптy(). Уколико је величина 0, метод враћа труе, иначе фалсе. Уколико АрраyЛист садржи само нулл елементе, то не значи да је празан тј да је величина 0 или да ће метод исЕмптy() вратити труе. Да би се испразнио елементи тј референце на објекте морају бити уклоњене, а не само постављене на нулл.

Претраживање АрраyЛист:

  • инт индеxОф(Т) – враћа позицију датог објекта у АрраyЛист
  • инт индеxОф(Т,инт) – враћа прву позицију датог објекта у АрраyЛист, почевши од позиције задате као други аргумент

За сортирање елемената колекције најбоље је имплементирати интерфејс Цомпарабле<>. Он декларише само један метод цомпареТо() – враћа -1, 0 или 1 ако је објекат мањи, једнак или већи од аргумента. Ако је Цомпарабле<> интерфејс имплементиран за тип објекта смештен у колекцији, можемо само објекат колекције предати као аргумент методу Цоллецтионс.сорт() интерфејса Цоллецтионс<>.

Повезана листа (ЛинкедЛист)

[уреди | уреди извор]

Констуктори

[уреди | уреди извор]
  • без аргумената
  • са аргументом Цоллецтион<> који креира ЛинкедЛист<> објекат који садржи објекте колекције која се прослеђује као аргумент
  • адд(),аддАлл() – као код класе Вецтор<>
  • аддФирст() – додаје објекат на почетак листе
  • аддЛаст() – додаје објекат на крај листе
  • гет(инт) – као код класе Вецтор<>
  • гетФирст(),гетЛаст()
  • ремове(),ремовеФирст(),ремовеЛаст()
  • сет()
  • сизе() – враћа број елемената листе

Меотдом итератор() добија се Итератор<> објекат над листом. Методом листИтератор() објекат ЛистИтератор<>, као и код класе Вецтор<>

Каталози

[уреди | уреди извор]

Каталог (ХасхМап<К,V>) такође називамо и речником. Заједно се чувају и кључ и објекат. Сваки објекат је јединствено одређен својим кључем. Сви кључеви, из тог разлога, морају да буду различити. Издвајање објеката из каталога врши се помоћу одговарајућег кључа, јер кључ одређује где се у каталогу налази објекат. Све класе за рад са каталозима имплементирају генерички интерфејс Мап<К,V>. Разматраћемо генеричку класу ХасхМап<К,V>.  Имплементација каталога помоћу генеричке класе ХасхМап<> подразумева да су парови кључ/објекат смештени у низ. Индекс у низу добија се на основу кључа за шта се користи метод хасхЦоде().Овај метод наслеђује се из класе Објецт и он производи подразумевани хешкод, осим ако није предефинисан у некој од изведених класа. Стога, било који објекат може да буде коришћен као кључ. На основу њега се методом хасхЦоде() генерише хешкод којим се одређује индекс пара са датим кључем у низу.

Хеширање

[уреди | уреди извор]

Ако желимо да као кључеве користимо објекте које сами дефинишемо, треба да предефинишемо метод еqуалс() из класе Објецт. Да би се обезбедила потребна функционалност, нова верзија треба да врати труе када два различита објекта садрже исте вредности. Такође, могуће је предефинисати метод хасхЦоде() тако да расподела буде прилично униформна на скупу могућих вредности за кључеве. Један начин је да се за сваки податак-чланицу класе генерише цео број нпр. постојећим методом хасхЦоде() који се затим множи простим бројем (сваки члан различитим) и на крају се добијени резултати сумирају. Генерисање целог броја за податак-чланицу класе се обично врши позивањем метода хасхЦоде(). Просте бројеве треба бирати тако да не буду превелики, како резултат не би био ван опсега за инт. Кад год је податак-чланица класе објекат неке друге класе, а не примитивног типа, неопходно је имплементирати хасхЦоде() метод за дату класу.

Констуктори за ХасхМап<>

[уреди | уреди извор]
  • ХасхМап() – подразумевани, креира каталог подразумеваног капацитета 16, а подразумевани лоад фактор је 0.75
  • ХасхМап(инт цапацитy) – креира каталог датог капацитета, са подразумеваним лоад фактором
  • ХасхМап(инт цапацитy, флоат лоадФацтор) – креира каталог са датим капацитетом и лоад фактором
  • четври констуктор креира каталог на основу постојећег каталога

Капацитет каталога је број парова кључ/објекат који могу да се чувају. Аутоматски се повећава, ако је неопходно.  Пожељно је да се за капацитет каталога задаје прост број, како би се избегла колизија. Фактор пуњења (лоад фактор) се користи за одлучивање када капацитет хеш табеле треба да се повећа . Када величина табеле достигне вредност производа фактора пуњења и капацитета, капацитет се аутоматски повећава два пута уз додавање 1 да би се осигурало да је нови капацитет барем непаран, ако већ није прост.

Смештање, добијање и уклањање објеката каталога:

  • V пут(К кеy, V валуе) – смешта објекат валуе у каталог користећи кључ кеy.
  • V ремове(Објецт кеy) – уклања пар повезан са кључем ако постоји и враћа референцу на објекат. Уколико не постоји одговарајући пар са датим кључем, или је објекат придружен кључу нулл, враћа се нулл.
  • V гет(Објецт кеy) – враћа објекат са истим кључем као кеy. Објекат остаје у каталогу. Ако нема ниједног објекта са датим кључем, или је нулл смештено уместо објекта, враћа се нулл

Ако гет() врати нулл, не знамо да ли објекат повезан са кључем не постоји или је објекат нулл. За ово служи метод цонтаинсКеy() који као аргумент има дати кључ. Он враћа труе ако је кључ смештен у каталогу.

Мап<> интерфејс обезбеђује 3 начина за добијање колекционог прегледа садржаја каталога:

  • кеyСет() – враћа Сет објекат који реферише на кључеве каталога
  • ентрyСет() – враћа Сет<Мап.Ентрy> објекат који реферише на парове кључ/објекат – сваки пар је објекат типа Мап.Ентрy. Ентрy је генерички интерфејсни тип дефинисан унутар интерфејса Мап<>.
  • валуес() – враћа Цоллецтион објекат који реферише на објекте из каталога.

Генеричке класе

[уреди | уреди извор]

На исти начин, као што се дефинишу генеричке функције, могу се дефинисати I генеричке класе. Дефиниција чланова класе се проводи помоћу генеричког типа. На пример

template<class T> class array {
    ...
}

помоћу које се може реализовати низови произвољног типа. На пример декларацијом

array<int> myints(5);

се формира низ од 5 целобројних елемената, а са

array<string> mystrings(5);

се формира низ од 5 стрингова.

template <class T> class array{
    T *m_pbuff;
    int m_size;
    public:
    array(int N = 10): 
        m_size(N) {
            m_pbuff=new T[N];
        }
    ~array() {
        delete []
        m_pbuff;
    }
    int size() {
        return m_size;
    }
    void set(int x, T value);
    T get(int x);
    T & operator [] (int i){
        return m_pbuff[i];
    }
    const T & operator [] (int i) const {
        return m_pbuff[i];
    }
}
template <class T> void array<T>::set(int x, T value){
    m_pbuff[x]=value;
}
template <class T> T array<T>::get(int x){
    return m_pbuff[x];
}

Гет() и сет() функције смо могли дефинисати инлине унутар дефиниције класе. Оне су дефинисане изван класе како би се показало да се тада увек испред дефиниције функције мора написати генерички префикс: темплате <цласс Т>.

int main () {
    array <int> myints(5);
    array<float> myfloats(5);
    myints.set (0, 100);
    myfloats.set(3, 3.1416);
    cout << "myints ima: " << myints.size() <<" elemenata"<< '\n';
    cout << myints[0] << '\n';
    cout << myfloats[3] << '\n';
    return 0;
}

Резултат је:

мyинтс има: 5 елемената

100

3.1416

Елементима низа се приступа помоћу сет/гет функција или помоћу индексне нотације.

Пример:

Класа паир служи као контењер два елемента који могу бити различитих типова. Формираћемо низ таквих парова помоћу генеричке класе арраy. У тај низ ћемо уписати и из њега исписати низ парова којима први елемент представља редни број (тип инт), а други елемент је квадратни корен првога (тип флоат).

template<class T1, class T2> class pair{
    T1 value1;
    T2 value2;
    public:
        pair (T1 first=0, T2 second=0){
            value1=first;
            value2=second;
        }
    T1 GetFirst () {return value1;}
    void SetFirst (T1 val) {
        value1 = val;
    }
    T2 GetSecond () {
        return value2;
    }
    void SetSecond (T2 val) {
        value2 = val;
    }
};
int main (){
    
// niz od 5 parova int,float
    array <pair<int,float> > arr(5);
    for(int i=0; i<arr.size(); i++){
        arr[i].SetFirst(i);//prvi sadrži redni broj
        arr[i].SetSecond(sqrt(i));//kvadratni koren prvog
    }
    for(int i=0; i<arr.size(); i++)
        cout<<arr[i].GetFirst() <<':'
    <<arr[i].GetSecond()<< endl;
    cout << endl;
    return 0;
}

Резултат је:

0:0
1:1
2:1.41421
3:1.73205
4:2

Важно је упозорити на облик декларације низа парова:

array <pair<int,float> > arr(5);
  1. Видимо да се конкретна реализација генеричког типа може извршити и помоћу других генеричких типова.
  2. У декларацији се појављују два знака > >. Између њих обвезно треба написати размак, јер ако би написали
    array <pair<int,float>> arr(5); // greška : >>
    
    компајлер би пријавио грешку због тога јер се знак >> третира као оператор.

Да би се избегле овакове грешке, препоручује се коришћење  тyпедеф декларације облика:

typedef pair <int,float> if_pair;
array <if_pair>  arr(5);

Специјализација шаблона

[уреди | уреди извор]

Стандардна библиотека шаблона[4] је софтверкса библиотеа програмског језика C++ која је делимично укључена у С++ стандардну библиотеку. Обезбеђује четири копмоненте: контејнере, итераторе, алгоритме и  функторе. СТЛ обезбеђује готов скуп основних класа за C++ као што су контејнери и асоцијативни низови,  који се може користити уз било који уграђени тип и уз било који кориснички дефинисан тип који подржава неке основне операције (као што су копирање и додела). СТЛ алгоритми су независни од котејнера, што значајно смањује комплексност библиотеке. СТЛ остварује резултате кроз употребу шаблона. Овај приступ обезбеђује полиморфизам времена компајлирања који је често ефикаснији од традиционалног полиморфизма времена покретања. Модерни C++ преводиоци подешени су тако да минимализују било какву казнз апстракције насталу интезивном употребом СТЛ-а. СТЛ је настао као прва библиотека генеричких алгоритама и структура података за C++, са четири идеје на уму: генеричко програмирање, апстракција без губитка ефикасности, фон Нојманова архитектура и вредносна семантика.

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

За класу коју дефинишемо шаблонон:

template<class gen_tip> identifikator{
    
}

Специјализација за конкретан тип се записује у облику:

template<>class identifikator<konkretan_tip>{
    
}

Специјализација се сматра делом шаблона, стога њена декларација започиње са тамплате<>. У њој се морају дефинисати сви чланови шаблчона, али искључиво са конкретним типовима за које се врши специјализација шаблона. Подразумева се да морају бити дефинисани конструктор и деструктор, уколико су дефинисани у општем шаблону.

Шаблони са константним параметрима

[уреди | уреди извор]

Параметри шаблона могу бити и интегралне константе (инт,цхар,лонг, унсигнед). На пример,

template <class T, int N>  class array {
    ...
}

је шаблон за дефинирање класе арраy помоћу генеричког типа Т и интегралне константе Н. Вредност интегралне константе се мора специфицирати при декларацији објекта. На пример,

array <float,5> myfloats;

декларише се мyфлоат као низ реалних бројева којем се додатна својства задају с константом Н=5. У следећем примеру користићемо овај константни параметар за постављање величине низа.

template <class T, int N>
class array{
    T m_buff[N]; // niz od N elemenata
    int m_size;
    public:
        array() : m_size(N) {}
    int size() {
        return m_size;
    }
    T & operator [] (int i) {
        return m_buff[i];
    }
    const T & operator [] (int i) const{ 
        return m_buff[i];
    }
};

int main () {
    array <int,5> myints;
    array<float,5> myfloats;
    myints[0]= 100;
    myfloats[3]= 3.1416; 13
    cout << "myints ima: "<<myints.size()
    <<" elemenata"<< '\n';
    cout << myints[0] << '\n';
    cout << myfloats[3] << '\n';
    return 0;
}

Добије се испис:

100
3.1416

Предодређени и функцијски параметри шаблона

[уреди | уреди извор]

У шаблонима се могу користити предодређени параметри. На пример, шаблоном

template <class T=int, int N=10> class array {
    
}

се омогућују декларације облика:

array<> a_int_10; // niz od 10 int
array<float> a_float_10; // niz od 10 float
array<char, 100> a_char_100; // niz od 100 char

Напомена: предодређени параметри се не смеју користити у функцијским шаблонима Параметри шаблона могу бити функције:

template <int Tfunc(int)>
class X {
    ...
    y = Tfunc(x); !!!
    ...
}

Морамо пазити да узимамо што мање претпоставки на типове који су параметри шаблона. Препоручује се: параметри генеричких функција су цонст референце (да бисмо избегли захтев да се објекти морају копирати, да постоји и да је добро дефинисан цопз конструктор), за упоређивање користимо само оператор < (не и оператор >, оператор >=, итд)

template <class T>
T min(const T &a, const T &b){
    return a < b ? a : b;
}

Поређење шаблона и макро наредби

[уреди | уреди извор]

Шаблоне можемо сматрати интегралним макро наредбама. Примена шаблона има неколико предности у односу на макро наредбе:

  1. Лакше их је схватити јер шаблони изгледају као регуларне класе(или функције)
  2. У развоју шаблона лакше се преводи тестирање с различитим типовима
  3. Компајлер осигурава знатно већу контролу грешала него је то случај с макро наредбама
  4. Функцијски шаблони имају дефинисан досег, јер се могу дефинисати као део класе (фриенд) или намеспаце
  5. Функцијски шаблони могу бити рекурзивни
  6. Функцијски шаблони се могу преоптеретити
  7. При комплајлирању неке датотеке, компајлер мора располагати с потпуном имплементацијом шаблона. Стога је уобичајено да се спецификација и имплементација функција записује у истој „.х“ датотеци. Макро карактер шаблона се посебно очитава код класа које имају статичке чланове. У том случају се за сваку реализацију шаблона иницира посебно статичка променљива (код регуларних се класа за све објекте једне класе генерише само једна статичка променљива)

Дефиниција и употреба класе твецтор<цласс Т>

[уреди | уреди извор]

Генеричка класа твецтор представља подскуп стандатдне класе вецтор. Намењено јој је манипулисање с хомогеним колекцијама елемената произвољног типа. Извршичемо спецификацију и имплемента класе твецтор темељем знања која смо стекли разматрајући генеричку класу арраy и класу Тстринг.

Објекти твецтор классе имају следеће карактеристике:

  • цапацитy() – капацитет вектор је број ћелија које се аутоматски алоцирају за спремање елемената вектора
  • сизе() – величина вектора је број елемената које стварно садржи вектор
  • ресизе(н) – ако је потребан већи капацитет вектора, он се може добити позивом функције ресизе(н)
  • оператор [] – поједином елементу вектора се приступа помоћу индексног оператора, или се користе две функције:
    • пусх_бацк(ел) додаје елемент на индексу иза последње уписаног елемента
    • поп_бацк(ел) брише последњи елемент из вектора

При позиву пусх_бацк() функције води се рачуна о капацитету вектора и врши се аутоматско алоцирање потребне меморије (меморија се удвостручује ако потребна величина вектора премашује тренутни капацитет). Намена ове две функције је да се вектор може користити као динамичка колекција елемената (контејнер).

Референце

[уреди | уреди извор]
  1. „Генеричке функције и класе – темплате (предлошци)”. Архивирано из оригинала на датум 2016-12-20. Приступљено 2019-01-17. 
  2. Генерички механизам
  3. „Генерички класни типови”. Архивирано из оригинала на датум 2016-12-20. Приступљено 2019-01-17. 
  4. Стандардна библиотека шаблона

У рачунарству, генеричко програмирање је техника која дозвољава да једна променљива може да чува различите типове података (такозвана вишеобличност или полиморфизам) све док су задовољени одређени услови као што су подкласа и правилна декларација. Дакле, дозвољава нам стварање функција и класа које не зависе од типа. Пример: СТЛ вектор, листа, стек итд. На пример, ако се жели направити листа користећи генеричност, могућа декларација би била Лист <Т>, где Т представља врсту података. Када се начини примерак може се направити Лист<Интегер> или Лист<Анимал>. Према листи се затим поступа као према листи оног типа података који је наведен. Од објектно оријентисаних програмских језика, програмски језици C++, D, БЕТА, Еиффел, Ада и неке верзије Јаве (1.5 и новије) подржавају генеричке типове података. ВБ.НЕТ и C# су почели да подржавају генеричке типове од верзије .НЕТ 2.0. Шаблони – основа за генеричко програмирање:  шаблон је уствари формула или рецепт за стварање класе или функције. Постоје функцијски шаблони и шаблони класе.

Генеричке функције и класе – темплате[1]

[уреди | уреди извор]

Под појмом генеричког програмирања подразумева се израда програмског кода који се у непромењеном облику може применити на различите типове података.

У C језику се за генеричко програмирање користе предпроцесорске макро наредбе, нпр:

#define Kvadrat(x) x*x

Ово је макро наредба којом се део кода који је у заградама означен са x замењује са x*x. Макро наредбе нису функције. Оне се реализују у току компајлирања лексичком заменом текста.

Пример:

Макро наредба: Извршава се као:
инт и;

Квадрат(и) * 3

инт и;

и*и*3

флоат x;

x*Квадрат(6.7)

флоат x;

x*6.7*6.7

флоат x;

x=Квадрат(x+6.7)

флоат x;

x=x+6.7*x+6.7

//грешка

У прва два примера показано је како се иста макро наредба користи за типове инт и флоат. Грешка у трећем примеру, се може избећи тако да се „аргументи“ макро наредбе напишу у заградама тј.

#define Kvadrat(x) (x)*(x)

Проблем код макроа је да је код макроа непрегледан и тешко је уочити цомпиле – грешке, будући да компајлер само код макроа налепи уместо позива и могуће су семантичке грешке које је врло тешко открити

То је један од разлога због којег у C++-у имамо генеричке (темплате) функције (функцијске шаблоне). C++ је строго типизирани језик, тј при дефиницији функције морамо навести типове параметара. C++ допушта коришћење преоптерећених (оверлоадед) функција. То су функције које имају једнака имена (и припадају истом досегу – намеспацеу) али различиту листу параметара. Преоптерећивање функције има неколико недостатака: уколико желимо нешто променити у коду функције морамо то учинити на пуно места, па се повећава и могућност грешке, не можемо предвидети на којим ће све типовима корисник хтети да позове функцију.

У C++ се исти ефекат као у C-у са #дефине може постићи коришћењем дефинисањем инлине функција:

inline int Kvadrat(int x) {
    return x*x;
}
inline float Kvadrat(float x) {
    return x*x;
}
inline double Kvadrat(double x) {
    return x*x;
}

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

T Kvadrat(T x) {
    return x*x;
}

где Т може бити инт, флоат или доубле. Ова дефиниција има генерички облик. У C++ језику се може вршити генеричко дефинисање функција, на начин да се испред дефиниције или декларације функције, помоћу кључне речи темплате, означи да Т представља „генерички тип“.

template <class T> T Kvadrat (T x) {
    return x*x;
}

или

template <typename T> T Kvadrat(T x) {
    return x*x;
}

Разлика у ова два записа је у употреби кључне речи цласс или тyпенаме, али значење је потпуно исто. Извршење позива функције се мора обавити са стварним типовима:

int x,y;
y=Kvadrat<int>(x);

Погодно је да се класе и функције могу писати генерички, параметризовано типовима података. Такве генеричке класе и функције називају се у језику C++  шаблонима (темплатес). Из шаблона се генеришу стварне класе, односно функције, за конкретне типове. Генерички механизам[2] је у потпуности статички -субституција параметара је у време превођења. Функцијски шаблон се не користи док компајлер не наиђе на позив генеричке функције. Тек тада се ствара и преводи нова варијанта функције у зависности о типу на којем је функција позвана. Тај процес стварања нове конкретне варијанте функције из функцијског шаблона назива се инстанцијација. Типови се код функцијских шаблона препознају аутоматски. Због тога се и дефиниција и имплементација таквих функција пише у .х датотеку. Компајлер тек у тренутку позива функције може одредити начин како се преводи генеричка функција, стога су генеричке функције по употреби  еквивалентне макро наредбама. Да би се могло извршити компајлирање програма у којем се користи генеричка функција, компајлеру мора бити на располагању потпуна дефиниција функције, а не само декларација функције као што је случај код употребе регуларних функција.

Генерички тип Т се може користити унутар функције за ознаку типа локалне променљиве:

template <class T> T Kvadrat(T x){
    T result=x*x;
    return result;
}
int main() {
    int i=5,k;                     
    float a=10.9,b;
    k=Kvadrat<int>(i);     
    b=Kvadrat<float>(a);
    cout <<k<<endl;
    cout<<b<<endl;
    return 0;
}
25
118.81

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

int i=5,k;
float a=10.9,b;
k=Kvadrat(i);
b=Kvadrat(a);
cout <<k<<endl;
cout<<b<<endl;

Препорука је ипак, да се увек експлицитно специфира параметарски тип. Могу се дефинисати генеричке функције са више генеричких типова. Пример:

template <class T, class U> t GetMin (T a, U b){
    return (a<b?a:b);
}

дефинише функцију са два аргумената којима се придружују генерички типови Т и У. Могуће је извршити позив функције на следећи начин: или још једноставније

i=GetMin (j,l);

Иако су ј и л различитог типа, компајлер сам врши конверзије интегралних типова.

Генерички класни типови[3]

[уреди | уреди извор]

Претпоставимо да радимо са неким скупом објеката које желимо да некако организујемо. У том случају користе се класе које се означавају као колекције (колекција=збирка, скупљање). Оне дефинишу објекте који се користе за организацију других објеката одговарајућег типа у колекције на одговарајући начин. Могуће је у колекцију додавати објекте различитог типа, али онда имамо проблем утврђивања типа објекта приликом његовог коришћења. Ако покушамо да неки објекат из ове колекције употребимо као објекат неке друге класе, наилазимо на проблем. Нпр. нека смо омогућили да колекцији могу да се додају објекти класа Тацка, Дуз и Стринг. Ако, примера ради, покушамо да употребимо неки објекат класе Дуз или Стринг као објекат класе Тацка, програм неће бити преведен. Да би се избегли овакви проблеми, треба дефинисати колекцију тако да се онемогући случајно додавање објекта погрешног типа. Један начин за решење овог проблема је дефинисати засебне колекције које могу да садрже само објекте једног типа. Али онда се јавља други проблем – имаћемо за сваки тип објекта по једну класу која дефинише одговарајућу колекцију. Други, ефикаснији начин, су управо генерички типови. Они омогућују да се дефинише само једна класа којом је колекција представљена. Сви објекти у колекцији су истог типа, али сада постоји могућност да тај тип може бити произвољан. На овај начин, имамо једну колекцију која може да се понаша и као колекција тачака, и као колекција дужи и као колекција стрингова итд. Генерички тип је класни или интерфејсни тип који има један или више параметара. Дефиниција конкретне класе или интерфејса из генеричког типа врши се додавањем конкретног аргумента за сваки од параметара генеричког типа.

public class LinkedList<T>{
    
}

Параметар Т се употребљава у дефиницији метода и поља где постоји зависност од типа овог аргумента. Појаве овог параметра у дефиницији генеричког типа се означавају као променљиве типа, јер  ће бити замењене конкретном вредношћу тог типа, на исти начин као што параметри метода бивају замењени аргументум који се проследи методу. Креирање класе из датог генеричког типа врши се навођењем одговарајућег аргумента за параметер унутар <>. Сва појављивања параметра Т у дефиницији генеричког типа биће замењена датим аргументум. Генерички тип на тај начин дефинише скуп типова, добијених за различите вредности параметра типа Т. Као аргумент за параметер Т може се проследити само класни или интерфејсни тип. Примитивни типови не могу да се користе. Уместо њих користимо одговарајуће класе које их енкапсулирају. Генерички тип ЛинкедЛист<Т> који чува доубле вредности:

LinkedList<Double> temp= new LinkedList<Double>();
temp.addItem(10.8)

Пошто је тип параметра Доубле компајлер аутоматски умеће конверзију вредности 10.8 из доубле у Доубле. Можемо да дефинишемо генерички тип који дефинише скуп класа које обухватају пар објеката произвољног типа. У том случају дефинишемо генерички тип са два параметра:

public class Par <TipKljuca, TipVrednosti> {

    public Par(TipKljuca kljuc, TipVrednosti vr) {

        this.kljuc = kljuc;
        this.vr = vr;
    }
    
    private TipKljuca kljuc;
    private TipVrednosti vr;
}

Употреба:

Par <String,String>unos =new Par<String,String>(Milan, 4445555);

Област важења параметарског типа је цела генеричка класа, искључујући статичке чланице класе. Статичка поља не могу бити дефинисана променљивом типа, нити статички методи могу да имају параметре или повратне вредности које су параметарског типа. Уколико генеричка класа садржи неко статичко поље, сваки тип произведен из датог генеричког типа, имаће своју копију статичког поља. Нека генеричка класа ЛинкедЛист<> садржи статичко поље цоунт за бројање креираних објеката. Свака класа добијена из генеричке за конкретан аргумент типа имаће своју копију овог поља. На тај начин,

цоунт у класи ЛинкедЛист<Стринг> броји креиране објекте ове класе

цоунт у класи ЛинкедЛист<Поинт> броји креиране објекте ове класе итд.

Генерички метод

[уреди | уреди извор]

Можемо да дефинишемо метод са својим независним скупом од једног или више параметара. Такав метод назива се параметарски метод или генерички метод. Генерички метод може да постоји и унутар обичне класе.

public static <T> void ispisiSve(LinkedList lista){
    for(T objekat: lista)
    System.out.println(objekat);
}

<Т> испред кога се наводи публиц или статиц кључна реч представља параметарски тип (или листу типова) за генерички метод. Овде имамо само један параметарски тип за метод исписиСве, али их може бити више. Листа параметарских типова се увек појављује између угластих заграда и наводи се иза квалификатора приступа, а пре повратне вредности метода. Аргумент типа ће бити одређен на основу параметра типа који је прослеђен при позиву метода.

Низови и параметризовани типови

[уреди | уреди извор]

Низови елемената типа добијеног од генеричког типа нису допуштени!

Колекције

[уреди | уреди извор]

Јава сyстем колекција је скуп генеричких типова које користимо за креирање колекцијских класа. Колекцијска класа је класа која организује скуп објеката датог типа на одређен начин (нпр. ланчане листе, стек,….). Највећи број ових класа које чине систем колекција дефинисане су у пакету јава.утил. Размотрићемо следеће генеричке типове:

  • Итератор<Т> интерфејсни тип - декларише методе за итерирање кроз један по један елемент колекције
  • АрраyЛист<Т> тип - има структуру динамичког низа, број објеката које можемо сачувати се аутоматски повећава по потреби.
  • ЛинкедЛист<Т> тип - двоструко повезана листа, омогућено је итерирање у оба смера

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

  • скупови
  • низови
  • каталози

Скуп(Сет) је најједноставнија колекција у којој објекти нису на неки специјалан начин уређени I објекти се једноставно додају скупу, без икакве контроле где иду. Можемо итерирати кроз све објекте скупа, испитати да ли је неки објекат члан скупа. Скуп не може да садржи дупликате. Објекат можемо и обрисати из скупа, али само ако имамо референце на њега у скупу.

Главна карактеристика низа је да су објекти смештени на линеаран начин, у произвољном, фиксном редоследу где имамо почетак и крај. Колекције у општем случају имају способност да се прошире да би се сместио потребан број објеката.Примери:

  • Низови
    • АрраyЛист, Вецтор
    • ЛинкедЛист
  • Редови
    • Стацк(ЛИФО)
    • Qуеуе(ФИФО)

Пошто је низ линеаран, могуће је додати нови објекат на почетак или крај или уметнути нови објекат иза дате позиције. Добијање објекта из низа се може извршити на висе начина:

  1. Може се селектовати први или последњи
  2. Може се добити објекат са дате позиције
  3. Може се претраживати низ унапред или уназад да би се испитало да ли се неки објекат налази у низу и слично

Итератор

[уреди | уреди извор]

Итератор је објекат који се користи за итерирање кроз колекцију, тј. приступ једном по једном објекту колекције. Употреба итератора омогуће употребу облика фор петље карактеристичног за претраживање колекција. Било који објекат који представља скуп или низ може да креира објекат типа Итератор<>. Објекат типа Итератор<> садржи референце на све објекте колекције у неком редоследу и њима се може приступити помоћу метода интерфејса Итератор<>:

  • Т неxт() -  враћа објекат типа Т и поставља Итератор<Т> објекат да при следећем позиву овог метода реферише на следећи објекат колекције. Ако нема објекта који ће бити враћен, избацује се изузетак типа НоСуцхЕлементЕxцептион
  • воид ремове() - брише последњи објекат враћен позивом метода неxт(). Ако неxт() није био позван или ако се позове ремове() два пута након позива неxт(), биће избачен изузетак типа ИллегалСтатеЕxцептион

Пример:

Класа примерак;

while(iterator.hasNext())
    primerak=iterator.next();

Овде се подразумева да је итератор типа Итератор<Класа> и да чува референце на објекат добијен из колекције. Један објекат који имплементира интерфејс Итератор<> је за једну итерацију кроз колекцију. Уколико је касније потребно поново проћи кроз све елементе колекције , неопходно је креирати нови објекат.

Лист итератори

[уреди | уреди извор]

У пакету јава.утил дефинисан је интерфејс ЛистИтератор<>. Декларише методе који се користе за кретање кроз колекцију напред и назад, тако да се до објекта може доћи висе него једном. ЛистИтератор<> интерфејс наслеђује Итератор<> интерфејс.

Списак метода:

  • Т неxт() -  као код Итератор<>
  • Боолеан хасНеxт() - као код Итератор<>
  • инт неxтИндеx() - враћа индекс објекта који ће бити враћен позивом метода неxт() као тип инт, или враћа број елемената у листи ако је *ЛистИтератор<> објекат на крају листе
  • Т превиоус() – враћа претходни објекат по редоследу у самој листи. Користимо га за враћање кроз листу.
  • Боолеан хасПревиоус() – враћа труе ако претходни позив превиоус() враћа објекат.
  • инт превиоусИндеx() - враћа индекс објекта који ће бити враћен следећим позивом метода превиоус() или -1 ако је ЛистИтератор<> објекат на почетку листе
  • воид ремове() - брише последњи објекат добијен позивом метода неxт() или превиоус(). Ако неxт() или превиоус() нису били позвани, избацује се изузетак типа ИллегалСтатеЕxцептион, а ако операција брисања није подржана за дату колекцију, избацује се изузетак типа УнсуппортедОператионЕxцептион
  • воид адд(Т обј) – додаје аргумент непосредно пре објекта који би био враћен следећем позивом метода неxт() или иза објекта који би био враћен следећим позивом метода превиоус(). Позив неxт() након додавања враћа додати елемент, а за превиоус() се ништа не мења. Ако објекат не може да се дода избавује се изузетак типа УнсуппортедОператионЕxцептион уколико постоји неки други разлог због којег додавање не може да се изврши.
  • воид сет(Т обј) – мења последњи објекат добијен позивом неxт() или превиоус(). I овај метод може да избаци изузетке типа УнсуппортедОператионЕxцептион, ЦлассЦастЕxцептион и ИллегалАргументЕxцептион.

АрраyЛист

[уреди | уреди извор]

АрраyЛист<Т> дефинише колекцију са елементима типа Т. Објекат АрраyЛист<Т> ради као и низ осим што додатно расте аутоматски, када је потребан већи капацитет. Као и низови, и АрраyЛист садрже референце на објекте, не сам објекте. То је правило за све колекције. АрраyЛист се, за разлику од низа, карактерише величином (сизе). Капацитет је максималан број објеката које АрраyЛист може да садржи. Капацитет је променљив, јер се аутоматски повећава када се дода нови објекат у већ пун вецтор. Методом енсуреЦапацитy(инт) поставља се минимални капацитет.

Пример:

v.ensureCapacity(150); /*ako je kapacitet objekta v manji od 150, kapacitet se uvećava na 150. AKo je 150 ili vise neće biti promenjen ovom naredbom.*/

Конструктори

[уреди | уреди извор]
  • АрраyЛист<>() – подразумевани конструктор креира празан АрраyЛист<> подразумеваног капацитета 10. Капацитет се повећава за 50% уколико се дода елемент у већ пун АрраyЛист.
  • АрраyЛист<Стринг> а= неw АрраyЛист<Стринг>();
  • АрраyЛист<Стринг> а= неw АрраyЛист<Стринг>(100); - експлицитно се задаје капацитет
  • сизе() – враћа број елемената који су смештени у АрраyЛист
  • адд(Т) -  додаје се објекат иза текућег последњег објекта у АрраyЛист
  • адд(инт,Т) – смешта се објекат на позицију задату првим аргументум, остали се померају за једно место у десно.
  • сет(инт,Т) – постављамо објекат у АрраyЛист на позицију задату првим аргументум, брише се објекат који је претходно био на тој позицији.
  • аддАлл(листа) – додају се сви објекти друге колекције у АрраyЛист
  • аддАлл(инт,листа)
  • гет(инт) – враћа елемент на задатој позицији

Чињеница да се капацитет АрраyЛист повећава за 50% сваки пут када се АрраyЛист попуни утиче на то да се меморија беспотребно заузима. Међутим, ово може да се превазиђе употребом метода тримТоСИзе(). Овај метод мења капацитет тако да одговара тренутној величини.

names.trimToSize(); /*postavlja  kapacitet na trenutnu veličinu. Ako je veličina u trenutku izvršavanja metoda 30 i kapacitet se postavlja na 30. Naravno i dalje se mogu dodavati novi objekti u ArrayList.*/

Можемо добити и ЛистИтератор референцу из вектора позивањем метода листИтератор().

ListIterator<String> it = names.listIterator(); /* sada je moguće kretati se u oba smera kroz ArrayList upotrebom metoda iz klase ListIterator. */
ListIterator <String>it = names.listIterator(2); /* ovim se dobija ListIterator objekat koji se odnosi samo na deo ArrayList, pri čemu je argument metoda listIterator() upravo prvi element date sekvence ArrayList. */
List <String>list = names.subList(2,5); /* dobija se podskup objekata iz ArrayList kao kolekcija tipa List<>. Argumetni metoda subList() su početak i kraj sekvence objekata koja čini podskup, pri čemu poslednji element nije uključen. */

Могуће су и комбинације:

ЛистИтератор<Стринг> листИтер = намес.субЛист(5,15).листИтератор(2); /* субЛист() враћа подлисту објеката АрраyЛист од позиције 5 до позиције 14 закључно. Након тога се за дату листу позива метод листИтератор() који враћа лист итератор над елементима листе почевши од елемента са индексом 2, па до краја. То одговара елементима полазног АрраyЛист са индексима од 7 до 14. Овим итератором се кроз дату секвенцу објеката полазног АрраyЛист може кретати у оба смера. */

Метод тоАрраy() омогућава да се елементи вектора добију у облику обичног низа.

String[] data = names.toArray(new String[names.size()]); /* argument metoda toArray() mora biti niz elemenata istog tipa kao i elementi ArrayList  ili nadtipa. Ako niz nije dovoljne veličine da primi sve elemente ArrayList, kreiraće se novi niz i referenca na taj niz će biti vraćena. Ovde metod toArray() vraća niz String[] koji sadrži sve elemente ArrayList names u odgovarajućem redosledu.*/
String[] people = {Bill, Barbara, Brian};
List<String> nameList = java.util.Arrays.asList(people);

Статички параметарски метод асЛист() дефинисан у класи јава.утил.Арраyс конвертује низ елемената датог типа Т у Лист<Т> колекцију. Аргумент метода је низ који се конвертује и враћа се референца типа Лист<Т>. Ова референца се може проследити као аргумент конструктору класе АрраyЛист:

ArrayList<String> names = new ArrayList(nameList); /* Time se dobija ArrayList koji sadrži elemente datog niza. */

Брисање елемената:

  • ремове(инт) – брисање референце на објекат на задатој позицији. Враћа се референца  на уклоњени објекат, тако да остаје могућност да се сачува референца након што се уклони из АрраyЛист.
  • ремове(Т) – брисање првог објекта са задатом референцом. Уколико је пронађен и уклоњен објекат, враћа се труе, иначе фалсе
  • ремовеАлл(намес.субЛист(2,5)); - бришу се елементи са индексима од 2 до 4
  • ретаилАлл(намес.субЛист(2,5)); - остају само елементи са индексима од 2 до 4
  • ремовеАллЕлементс() – уклања све елементе из АрраyЛист

Да ли је АрраyЛист празан може се утврдити методом исЕмптy(). Уколико је величина 0, метод враћа труе, иначе фалсе. Уколико АрраyЛист садржи само нулл елементе, то не значи да је празан тј да је величина 0 или да ће метод исЕмптy() вратити труе. Да би се испразнио елементи тј референце на објекте морају бити уклоњене, а не само постављене на нулл.

Претраживање АрраyЛист:

  • инт индеxОф(Т) – враћа позицију датог објекта у АрраyЛист
  • инт индеxОф(Т,инт) – враћа прву позицију датог објекта у АрраyЛист, почевши од позиције задате као други аргумент

За сортирање елемената колекције најбоље је имплементирати интерфејс Цомпарабле<>. Он декларише само један метод цомпареТо() – враћа -1, 0 или 1 ако је објекат мањи, једнак или већи од аргумента. Ако је Цомпарабле<> интерфејс имплементиран за тип објекта смештен у колекцији, можемо само објекат колекције предати као аргумент методу Цоллецтионс.сорт() интерфејса Цоллецтионс<>.

Повезана листа (ЛинкедЛист)

[уреди | уреди извор]

Констуктори

[уреди | уреди извор]
  • без аргумената
  • са аргументом Цоллецтион<> који креира ЛинкедЛист<> објекат који садржи објекте колекције која се прослеђује као аргумент
  • адд(),аддАлл() – као код класе Вецтор<>
  • аддФирст() – додаје објекат на почетак листе
  • аддЛаст() – додаје објекат на крај листе
  • гет(инт) – као код класе Вецтор<>
  • гетФирст(),гетЛаст()
  • ремове(),ремовеФирст(),ремовеЛаст()
  • сет()
  • сизе() – враћа број елемената листе

Меотдом итератор() добија се Итератор<> објекат над листом. Методом листИтератор() објекат ЛистИтератор<>, као и код класе Вецтор<>

Каталози

[уреди | уреди извор]

Каталог (ХасхМап<К,V>) такође називамо и речником. Заједно се чувају и кључ и објекат. Сваки објекат је јединствено одређен својим кључем. Сви кључеви, из тог разлога, морају да буду различити. Издвајање објеката из каталога врши се помоћу одговарајућег кључа, јер кључ одређује где се у каталогу налази објекат. Све класе за рад са каталозима имплементирају генерички интерфејс Мап<К,V>. Разматраћемо генеричку класу ХасхМап<К,V>.  Имплементација каталога помоћу генеричке класе ХасхМап<> подразумева да су парови кључ/објекат смештени у низ. Индекс у низу добија се на основу кључа за шта се користи метод хасхЦоде().Овај метод наслеђује се из класе Објецт и он производи подразумевани хешкод, осим ако није предефинисан у некој од изведених класа. Стога, било који објекат може да буде коришћен као кључ. На основу њега се методом хасхЦоде() генерише хешкод којим се одређује индекс пара са датим кључем у низу.

Хеширање

[уреди | уреди извор]

Ако желимо да као кључеве користимо објекте које сами дефинишемо, треба да предефинишемо метод еqуалс() из класе Објецт. Да би се обезбедила потребна функционалност, нова верзија треба да врати труе када два различита објекта садрже исте вредности. Такође, могуће је предефинисати метод хасхЦоде() тако да расподела буде прилично униформна на скупу могућих вредности за кључеве. Један начин је да се за сваки податак-чланицу класе генерише цео број нпр. постојећим методом хасхЦоде() који се затим множи простим бројем (сваки члан различитим) и на крају се добијени резултати сумирају. Генерисање целог броја за податак-чланицу класе се обично врши позивањем метода хасхЦоде(). Просте бројеве треба бирати тако да не буду превелики, како резултат не би био ван опсега за инт. Кад год је податак-чланица класе објекат неке друге класе, а не примитивног типа, неопходно је имплементирати хасхЦоде() метод за дату класу.

Констуктори за ХасхМап<>

[уреди | уреди извор]
  • ХасхМап() – подразумевани, креира каталог подразумеваног капацитета 16, а подразумевани лоад фактор је 0.75
  • ХасхМап(инт цапацитy) – креира каталог датог капацитета, са подразумеваним лоад фактором
  • ХасхМап(инт цапацитy, флоат лоадФацтор) – креира каталог са датим капацитетом и лоад фактором
  • четври констуктор креира каталог на основу постојећег каталога

Капацитет каталога је број парова кључ/објекат који могу да се чувају. Аутоматски се повећава, ако је неопходно.  Пожељно је да се за капацитет каталога задаје прост број, како би се избегла колизија. Фактор пуњења (лоад фактор) се користи за одлучивање када капацитет хеш табеле треба да се повећа . Када величина табеле достигне вредност производа фактора пуњења и капацитета, капацитет се аутоматски повећава два пута уз додавање 1 да би се осигурало да је нови капацитет барем непаран, ако већ није прост.

Смештање, добијање и уклањање објеката каталога:

  • V пут(К кеy, V валуе) – смешта објекат валуе у каталог користећи кључ кеy.
  • V ремове(Објецт кеy) – уклања пар повезан са кључем ако постоји и враћа референцу на објекат. Уколико не постоји одговарајући пар са датим кључем, или је објекат придружен кључу нулл, враћа се нулл.
  • V гет(Објецт кеy) – враћа објекат са истим кључем као кеy. Објекат остаје у каталогу. Ако нема ниједног објекта са датим кључем, или је нулл смештено уместо објекта, враћа се нулл

Ако гет() врати нулл, не знамо да ли објекат повезан са кључем не постоји или је објекат нулл. За ово служи метод цонтаинсКеy() који као аргумент има дати кључ. Он враћа труе ако је кључ смештен у каталогу.

Мап<> интерфејс обезбеђује 3 начина за добијање колекционог прегледа садржаја каталога:

  • кеyСет() – враћа Сет објекат који реферише на кључеве каталога
  • ентрyСет() – враћа Сет<Мап.Ентрy> објекат који реферише на парове кључ/објекат – сваки пар је објекат типа Мап.Ентрy. Ентрy је генерички интерфејсни тип дефинисан унутар интерфејса Мап<>.
  • валуес() – враћа Цоллецтион објекат који реферише на објекте из каталога.

Генеричке класе

[уреди | уреди извор]

На исти начин, као што се дефинишу генеричке функције, могу се дефинисати I генеричке класе. Дефиниција чланова класе се проводи помоћу генеричког типа. На пример

template<class T> class array {
    ...
}

помоћу које се може реализовати низови произвољног типа. На пример декларацијом

array<int> myints(5);

се формира низ од 5 целобројних елемената, а са

array<string> mystrings(5);

се формира низ од 5 стрингова.

template <class T> class array{
    T *m_pbuff;
    int m_size;
    public:
    array(int N = 10): 
        m_size(N) {
            m_pbuff=new T[N];
        }
    ~array() {
        delete []
        m_pbuff;
    }
    int size() {
        return m_size;
    }
    void set(int x, T value);
    T get(int x);
    T & operator [] (int i){
        return m_pbuff[i];
    }
    const T & operator [] (int i) const {
        return m_pbuff[i];
    }
}
template <class T> void array<T>::set(int x, T value){
    m_pbuff[x]=value;
}
template <class T> T array<T>::get(int x){
    return m_pbuff[x];
}

Гет() и сет() функције смо могли дефинисати инлине унутар дефиниције класе. Оне су дефинисане изван класе како би се показало да се тада увек испред дефиниције функције мора написати генерички префикс: темплате <цласс Т>.

int main () {
    array <int> myints(5);
    array<float> myfloats(5);
    myints.set (0, 100);
    myfloats.set(3, 3.1416);
    cout << "myints ima: " << myints.size() <<" elemenata"<< '\n';
    cout << myints[0] << '\n';
    cout << myfloats[3] << '\n';
    return 0;
}

Резултат је:

мyинтс има: 5 елемената

100

3.1416

Елементима низа се приступа помоћу сет/гет функција или помоћу индексне нотације.

Пример:

Класа паир служи као контењер два елемента који могу бити различитих типова. Формираћемо низ таквих парова помоћу генеричке класе арраy. У тај низ ћемо уписати и из њега исписати низ парова којима први елемент представља редни број (тип инт), а други елемент је квадратни корен првога (тип флоат).

template<class T1, class T2> class pair{
    T1 value1;
    T2 value2;
    public:
        pair (T1 first=0, T2 second=0){
            value1=first;
            value2=second;
        }
    T1 GetFirst () {return value1;}
    void SetFirst (T1 val) {
        value1 = val;
    }
    T2 GetSecond () {
        return value2;
    }
    void SetSecond (T2 val) {
        value2 = val;
    }
};
int main (){
    
// niz od 5 parova int,float
    array <pair<int,float> > arr(5);
    for(int i=0; i<arr.size(); i++){
        arr[i].SetFirst(i);//prvi sadrži redni broj
        arr[i].SetSecond(sqrt(i));//kvadratni koren prvog
    }
    for(int i=0; i<arr.size(); i++)
        cout<<arr[i].GetFirst() <<':'
    <<arr[i].GetSecond()<< endl;
    cout << endl;
    return 0;
}

Резултат је:

0:0
1:1
2:1.41421
3:1.73205
4:2

Важно је упозорити на облик декларације низа парова:

array <pair<int,float> > arr(5);
  1. Видимо да се конкретна реализација генеричког типа може извршити и помоћу других генеричких типова.
  2. У декларацији се појављују два знака > >. Између њих обвезно треба написати размак, јер ако би написали
    array <pair<int,float>> arr(5); // greška : >>
    
    компајлер би пријавио грешку због тога јер се знак >> третира као оператор.

Да би се избегле овакове грешке, препоручује се коришћење  тyпедеф декларације облика:

typedef pair <int,float> if_pair;
array <if_pair>  arr(5);

Специјализација шаблона

[уреди | уреди извор]

Стандардна библиотека шаблона[4] је софтверкса библиотеа програмског језика C++ која је делимично укључена у С++ стандардну библиотеку. Обезбеђује четири копмоненте: контејнере, итераторе, алгоритме и  функторе. СТЛ обезбеђује готов скуп основних класа за C++ као што су контејнери и асоцијативни низови,  који се може користити уз било који уграђени тип и уз било који кориснички дефинисан тип који подржава неке основне операције (као што су копирање и додела). СТЛ алгоритми су независни од котејнера, што значајно смањује комплексност библиотеке. СТЛ остварује резултате кроз употребу шаблона. Овај приступ обезбеђује полиморфизам времена компајлирања који је често ефикаснији од традиционалног полиморфизма времена покретања. Модерни C++ преводиоци подешени су тако да минимализују било какву казнз апстракције насталу интезивном употребом СТЛ-а. СТЛ је настао као прва библиотека генеричких алгоритама и структура података за C++, са четири идеје на уму: генеричко програмирање, апстракција без губитка ефикасности, фон Нојманова архитектура и вредносна семантика.

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

За класу коју дефинишемо шаблонон:

template<class gen_tip> identifikator{
    
}

Специјализација за конкретан тип се записује у облику:

template<>class identifikator<konkretan_tip>{
    
}

Специјализација се сматра делом шаблона, стога њена декларација започиње са тамплате<>. У њој се морају дефинисати сви чланови шаблчона, али искључиво са конкретним типовима за које се врши специјализација шаблона. Подразумева се да морају бити дефинисани конструктор и деструктор, уколико су дефинисани у општем шаблону.

Шаблони са константним параметрима

[уреди | уреди извор]

Параметри шаблона могу бити и интегралне константе (инт,цхар,лонг, унсигнед). На пример,

template <class T, int N>  class array {
    ...
}

је шаблон за дефинирање класе арраy помоћу генеричког типа Т и интегралне константе Н. Вредност интегралне константе се мора специфицирати при декларацији објекта. На пример,

array <float,5> myfloats;

декларише се мyфлоат као низ реалних бројева којем се додатна својства задају с константом Н=5. У следећем примеру користићемо овај константни параметар за постављање величине низа.

template <class T, int N>
class array{
    T m_buff[N]; // niz od N elemenata
    int m_size;
    public:
        array() : m_size(N) {}
    int size() {
        return m_size;
    }
    T & operator [] (int i) {
        return m_buff[i];
    }
    const T & operator [] (int i) const{ 
        return m_buff[i];
    }
};

int main () {
    array <int,5> myints;
    array<float,5> myfloats;
    myints[0]= 100;
    myfloats[3]= 3.1416; 13
    cout << "myints ima: "<<myints.size()
    <<" elemenata"<< '\n';
    cout << myints[0] << '\n';
    cout << myfloats[3] << '\n';
    return 0;
}

Добије се испис:

100
3.1416

Предодређени и функцијски параметри шаблона

[уреди | уреди извор]

У шаблонима се могу користити предодређени параметри. На пример, шаблоном

template <class T=int, int N=10> class array {
    
}

се омогућују декларације облика:

array<> a_int_10; // niz od 10 int
array<float> a_float_10; // niz od 10 float
array<char, 100> a_char_100; // niz od 100 char

Напомена: предодређени параметри се не смеју користити у функцијским шаблонима Параметри шаблона могу бити функције:

template <int Tfunc(int)>
class X {
    ...
    y = Tfunc(x); !!!
    ...
}

Морамо пазити да узимамо што мање претпоставки на типове који су параметри шаблона. Препоручује се: параметри генеричких функција су цонст референце (да бисмо избегли захтев да се објекти морају копирати, да постоји и да је добро дефинисан цопз конструктор), за упоређивање користимо само оператор < (не и оператор >, оператор >=, итд)

template <class T>
T min(const T &a, const T &b){
    return a < b ? a : b;
}

Поређење шаблона и макро наредби

[уреди | уреди извор]

Шаблоне можемо сматрати интегралним макро наредбама. Примена шаблона има неколико предности у односу на макро наредбе:

  1. Лакше их је схватити јер шаблони изгледају као регуларне класе(или функције)
  2. У развоју шаблона лакше се преводи тестирање с различитим типовима
  3. Компајлер осигурава знатно већу контролу грешала него је то случај с макро наредбама
  4. Функцијски шаблони имају дефинисан досег, јер се могу дефинисати као део класе (фриенд) или намеспаце
  5. Функцијски шаблони могу бити рекурзивни
  6. Функцијски шаблони се могу преоптеретити
  7. При комплајлирању неке датотеке, компајлер мора располагати с потпуном имплементацијом шаблона. Стога је уобичајено да се спецификација и имплементација функција записује у истој „.х“ датотеци. Макро карактер шаблона се посебно очитава код класа које имају статичке чланове. У том случају се за сваку реализацију шаблона иницира посебно статичка променљива (код регуларних се класа за све објекте једне класе генерише само једна статичка променљива)

Дефиниција и употреба класе твецтор<цласс Т>

[уреди | уреди извор]

Генеричка класа твецтор представља подскуп стандатдне класе вецтор. Намењено јој је манипулисање с хомогеним колекцијама елемената произвољног типа. Извршичемо спецификацију и имплемента класе твецтор темељем знања која смо стекли разматрајући генеричку класу арраy и класу Тстринг.

Објекти твецтор классе имају следеће карактеристике:

  • цапацитy() – капацитет вектор је број ћелија које се аутоматски алоцирају за спремање елемената вектора
  • сизе() – величина вектора је број елемената које стварно садржи вектор
  • ресизе(н) – ако је потребан већи капацитет вектора, он се може добити позивом функције ресизе(н)
  • оператор [] – поједином елементу вектора се приступа помоћу индексног оператора, или се користе две функције:
    • пусх_бацк(ел) додаје елемент на индексу иза последње уписаног елемента
    • поп_бацк(ел) брише последњи елемент из вектора

При позиву пусх_бацк() функције води се рачуна о капацитету вектора и врши се аутоматско алоцирање потребне меморије (меморија се удвостручује ако потребна величина вектора премашује тренутни капацитет). Намена ове две функције је да се вектор може користити као динамичка колекција елемената (контејнер).

Референце

[уреди | уреди извор]
  1. „Генеричке функције и класе – темплате (предлошци)”. Архивирано из оригинала на датум 2016-12-20. Приступљено 2019-01-17. 
  2. Генерички механизам
  3. „Генерички класни типови”. Архивирано из оригинала на датум 2016-12-20. Приступљено 2019-01-17. 
  4. Стандардна библиотека шаблона