Kliknite tukaj, da si ogledate profil, kot ga vidijo drugi

Šablone funkcij in razredov

Delite vsebino na Facebooku
Delite vsebino na Twitterju
  V tej lekciji bomo spoznali polimorfizem. Z njim bomo lahko uvedli abstrakcijo, da bodo funkcije in razredi sprejeli argumente različnih tipov.
 
 
1
 
 

Želimo napisati funkcijo, ki bo zračunala vsoto katerega koli polja. Kako bi se lotili? Brez predznanja šablon bi rešitev zgledala nekako takole:

int SUM(int polje[], int size)
{

int suma=0;

for(int i=0;i<size;i++)

suma+=polje[i];

return suma;

}


double SUM(double polje[],double size)
{

double suma=0;

for(int i=0;i<size;i++)

suma+=polje[i];

return suma;

}

string SUM(string polje[],int size)
{

string suma="";

for(int i=0;i<size;i++)

suma+=polje[i];

return suma;

}

Za vsak različen tip smo morali napisati svojo funkcijo. Morda ne zgleda tako pomembno, saj je funkcija preprosta. Vendar kaj v primeru, kadar je algoritem dolg preko 500 vrstic?

Kaj če smo kje opazili napako? Morali bi spremeniti vsako funkcijo. Vzdržavanje in pregled kode je nadležen. Uporaba generičnega kazalca (void *) pa ni dobra rešitev, saj hitro vodi do "napak" nepoznavalca.

 
 
2
 
 

Rešitev je uporaba šablon (ang. template)!

template<typename TIP>

TIP sestejPolje(TIP polje[], int size)

{

TIP suma=0;

for(int i=0;i<size;i++)
{

suma+=polje[i];

return suma;

}

 

template<> 

string sestejPolje(string polje[],int size)

{

string suma="";

for(int i=0;i<size;i++)

suma+=polje[i];

return suma;

}

Šablono napišemo pred funkcijo nad katero se naj uveljavi. "typename TIP" pa nam pove da bo univerzalna spremenljivka naziva TIP

TIP suma=0;

Nam pove da bo spremenljivka suma tipa, kateri je bil podan kot šablona.


cout<<sestejPolje(dPolje,4);
//dPolje je polja tipa double

Prevajalnik bo prepoznam katerega tipa je dPolje in zato kot template določil, da bo TIP -> double.

//če tega ne bi mogel, zaradi kakšnega dvoumentega parametra, pa lahko vedno najavimo tip eksplicitno!

cout<<sestejPolje<double>(dPolje,4);



template<>
Le kaj to pomeni?

Ker stringu ob inicializaciji ne moremo prirediti 0,
(konstruktor ga avtomatsko nastavi na prazni niz) mora koda izgledati malce drugače. Še vedno pa se mora obnašati kot prejšna funkcija oz. nosi isto ime.

S tem najavimo da mora prevajalnik pogledati kakšnega tipa je vhodni argument.

template<> 

string sestejPolje(string polje[],int size)



In če je ta tipa string, potem mora uporabiti to funkcijo! Saj je ta eksplicitno rezervirana zanj.




Primer:

template<typename TIP>

TIP sestejPolje(TIP polje[], int size)

{

TIP suma=0;

for(int i=0;i<size;i++)

suma+=polje[i];

return suma;

}

 

template<> 

string sestejPolje(string polje[],int size)
{

string suma="";

for(int i=0;i<size;i++)

suma+=polje[i];

return suma;

}

int main()
{

double dPolje[] = {0.1,0.2,0.3,0.4};

int iPolje[] ={1,2,3,4};

string sPolje[] ={"1","2","3","4"};

 

 

cout<<sestejPolje<double>(dPolje,4)<<endl<<sestejPolje(iPolje,4)<<endl<<sestejPolje(sPolje,4)<<endl;
cin.get();
return 0;
}

 
 
3
 
 

Šablone imajo tudi razredi!



template<typename TIP1, typename TIP2=TIP1

struct PAR

{

TIP1 p1;

TIP2 p2;

PAR(){}

PAR(TIP1 p1, TIP2 p2):p1(p1),p2(p2){}

};

Imemo nek preprost razred PAR. Ta lahko nosi dve spremenljivki različnega tipa.

template<typename TIP1, typename TIP2=TIP1>

Kar je potrebno povdariti tukaj je krepek del kode.
Obnaša se kot privzeta vrednost. Kadar šabloni razreda ne pripišemo drugega tipa, bo TIP2 tipa TIP1.


Primer:

PAR <string>par;

PAR.p1 in PAR.p2 bosta oba tipa string!

par.p1 = "Ena";

par.p2 = "Dva";

Celoten primer:

template<typename TIP1, typename TIP2=TIP1> 

struct PAR

{

TIP1 p1;

TIP2 p2;

PAR(){}

PAR(TIP1 p1, TIP2 p2):p1(p1),p2(p2){}

 

};

int main()
{

        PAR <string>par;

PAR <int,string>oseba;

par.p1 = "Ena";

par.p2 = "Dva";

oseba.p1 = 20;

oseba.p2 = "Janez";

    cout<<par.p1<<" -> "<<par.p2<<endl;

cout<<oseba.p2<<" je star "<<oseba.p1<<" let."<<endl;

 cin.get();
 return 0;
}

 
 
4
 
 

Dodatno:

Šablona kot parameter

#include <vector>

template<typename TIP1, typename TIP2=TIP1> 

struct PAR

{

TIP1 p1;

TIP2 p2;

PAR(){}

PAR(TIP1 p1, TIP2 p2):p1(p1),p2(p2){}

};


template <typename TIP1,
              template
              <
                typename t1, typename t2=t1
            > class T>

struct VECTOR_PAR{

vector<T<TIP1>>PAR;

};

 

int main()
{
  VECTOR_PAR<double,PAR> nov_PAR;

nov_PAR.PAR.push_back(PAR<double>(3.3,3.2));

nov_PAR.PAR.push_back(PAR<double>(3.2,2.2));

cin.get();
        return 0;

}


template <typename TIP1, 
              template
              <
                typename t1, typename t2=t1
            > class T>

Tukaj opazimo da v šablono pošljemo še eno šablono. Povemo, da bo šablona T razred kateri bo imel za definicijo šablone dva tipa (en je privzet).

struct VECTOR_PAR{

vector<T<TIP1>>PAR;

};

Tako bomo izrabili podatkovno strukturo vektor in mu določili da sprejme razred (kateri je prišel kot šablona T), katerega tip šablone je šablona TIP1.
Pravkar smo gnezdili šablone.



Na tak način bi lahko sedaj ustvarili objekt razreda VECTOR_PAR, ki bi sprejel različne objekte kateri imajo šablono, in prav tako pisali manj kode, kot pa nov razred za vsako spremembo!






 
 
5
 
 

Celotna koda:

#include <iostream>

#include <string>

#include <vector>

using namespace std;

 

template<typename TIP>

TIP sestejPolje(TIP polje[], int size)

{

TIP suma=0;

for(int i=0;i<size;i++)

suma+=polje[i];

return suma;

}

 

template<> 

string sestejPolje(string polje[],int size)

{

string suma="";

for(int i=0;i<size;i++)

suma+=polje[i];

return suma;

}

 

template<typename TIP1, typename TIP2=TIP1> 

struct PAR

{

TIP1 p1;

TIP2 p2;

PAR(){}

PAR(TIP1 p1, TIP2 p2):p1(p1),p2(p2){}

};

 

template <typename TIP1, 

                template

              <

                typename t1, typename t2=t1

              > class T>

struct VECTOR_PAR{

vector<T<TIP1>>PAR;

};

 

 

 

int main()

{

 

double dPolje[] = {0.1,0.2,0.3,0.4};

int iPolje[] ={1,2,3,4};

string sPolje[] ={"1","2","3","4"};

 

cout<<sestejPolje<double>(dPolje,4)<<endl<<sestejPolje(iPolje,4)<<endl<<sestejPolje(sPolje,4)<<endl;

 

PAR <string>par;

PAR <int,string>oseba;

par.p1 = "Ena";

par.p2 = "Dva";

oseba.p1 = 20;

oseba.p2 = "Janez";

    cout<<par.p1<<" -> "<<par.p2<<endl;

cout<<oseba.p2<<" je star "<<oseba.p1<<" let."<<endl;

 

VECTOR_PAR<double,PAR> nov_PAR;

nov_PAR.PAR.push_back(PAR<double>(3.3,3.2));

nov_PAR.PAR.push_back(PAR<double>(3.2,2.2));

cin.get();

return 0;

}

 
 
Komentiraj
 
 
Prijava in registracija
 
 
 
Zmaga.com ponuja brezplačno in razumljivo učenje računalniških programov, vas na enkraten način spozna z različnimi svetovnimi jeziki, s podrobno obrazloženimi recepti prikaže čare kulinarike in vam prežene strahove pred domačimi opravili.
 
 
Poleg tega lahko prebirate poučne članke, ki so namenjene širjenju naše splošne razgledanosti ter preverite svoje znanje z priljubljenim in enostavnim sistemom za preverjanje znanja. Če med vsebinami, ki se dodajajo vsak dan, ne najdete želenega znanja, je za vaša vprašanja na voljo dobro obiskan forum, kjer lahko tudi aktivno sodelujete. V primeru, da bi radi svoje praktično znanje delili z ostalimi, pa to lahko storite preko preprostega vmesnika za dodajanje vsebin. Zmagajte z znanjem z Zmaga.com!