Malli struktuur c. Mallid

10.1. Funktsioonimalli määratlemine

Vahel võib tunduda, et tugevasti trükitud keel muudab üldse millegi rakendamise keeruliseks. lihtsad funktsioonid. Näiteks kuigi järgmine algoritm Funktsioon min () on triviaalne, tugev tippimine nõuab, et selle variatsioonid rakendataks kõigi võrreldavate tüüpide jaoks:

int min(int a, int b) (

tagastab a b ? a: b;

double min(double a, double b) (

tagastab a b ? a: b;

Ahvatlev alternatiiv iga eksemplari min() selgesõnaliseks määratlemiseks on kasutada eelprotsessoriga laiendatavaid makrosid:

#define min(a, b) ((a) (b) ? (a) : (b))

Kuid selline lähenemine on täis võimalikke ohte. Eespool defineeritud makro töötab õigesti ka min() lihtsate väljakutsetega, näiteks:

min(10,0, 20,0);

kuid see võib tuua üllatusi keerulisematel juhtudel: selline mehhanism ei käitu nagu funktsioonikutse, vaid teostab vaid tekstilist argumentide asendust. Selle tulemusena hinnatakse mõlema argumendi väärtusi kaks korda: üks kord a ja b võrdlemisel ning üks kord makro tagastatud tulemuse arvutamisel:

#include iostream

#define min(a,b) ((a) (b) ? (a) : (b))

const int suurus = 10;

while (min(p++,ia) != ia)

cout "elem_cnt: "elem_cnt

" ootus: " suurus endl;

Esmapilgul loendab see programm elementide arvu täisarvude massiivi ia. Kuid sel juhul laieneb makro min() valesti, kuna suurendamisjärgset toimingut rakendatakse kursori argumendile iga asendamise korral kaks korda. Selle tulemusena prindib programm valedele arvutustele viitava rea:

elem_cnt: 5 ootus: 10

Funktsioonimallid pakuvad mehhanismi, mille abil saame säilitada funktsioonide definitsioonide ja väljakutsete semantika (kapseldades koodilõigu ühte kohta programmis ja tagades, et argumente hinnatakse üks kord), ohverdamata C++ keele tugevat tippimist, nagu on makrode puhul.

Mall annab kasutatud algoritmi automaatne genereerimine funktsiooni eksemplarid koos erinevad tüübid. Programmeerija määrab funktsiooni liideses parameetrid kõik või ainult mõned tüübid (st formaalse parameetri ja tagastusväärtuse tüübid), jättes selle keha muutmata. Funktsioon sobib hästi malliks, kui selle rakendamine jääb muutumatuks teatud eksemplaride hulgas, mis erinevad andmetüüpide poolest, nagu näiteks min() puhul.

Funktsiooni min() mall määratletakse järgmiselt:

malli klassi tüüp

Tüüp min2 (tüüp a, tüüp b) (

tagastab a b ? a: b;

// õige: min(int, int);

// õige: min(double, double);

min(10,0, 20,0);

Kui eelprotsessori asemel on tekstis asendatud makro min(). eelmine programm see muster, siis on tulemus õige:

elem_cnt: 10 ootus: 10

(IN standardne raamatukogu C++-l on funktsioonimallid paljudele sagedamini kasutatavatele algoritmidele, näiteks min(). Neid algoritme kirjeldatakse peatükis 12. Ja selles sissejuhatavas peatükis esitame mõnede standardteegi algoritmide lihtsustatud versioonid.)

Nii funktsioonimalli deklareerimine kui ka määratlus peavad alati algama märksõna malliga, millele järgneb komadega eraldatud identifikaatorite loend, mis on ümbritsetud nurksulgudega " ja ", malli parameetrite loend, mis ei tohi olla tühi. Mallil võivad olla tüübiparameetrid, mis tähistavad tüüpi, ja konstantsed parameetrid, mis esindavad fikseeritud konstantset avaldist.

Tüübiparameeter koosneb klassi märksõnast või tüübinime märksõnast, millele järgneb identifikaator. Need sõnad näitavad alati, et järgmine nimi viitab sisseehitatud või kasutaja määratud tüübile. Malli parameetri nime valib programmeerija. Ülaltoodud näites kasutasime nime Tüüp, kuid oleksime võinud valida midagi muud:

malli klass Glorp

Glorp min2(Glorp a, Glorp b) (

tagastab a b ? a: b;

Malli instantimisel (konkreetse eksemplari loomisel) asendatakse tüübiparameetriga tegelik sisseehitatud või kasutaja määratud tüüp. Iga tüüp int, double, char*, vectorint või listdouble on kehtiv malliargument.

Konstantne parameeter näeb välja nagu tavaline deklaratsioon. See ütleb, et parameetri nime asemel tuleks asendada malli definitsiooni konstantne väärtus. Näiteks suurus on konstantne parameeter, mis tähistab massiivi arr suurust:

malli klass Tüüp, int suurus

Tüüp min(Tüüp (arr) );

Malli parameetrite loendi järel on funktsiooni deklaratsioon või definitsioon. Kui te ei pööra tähelepanu parameetrite olemasolule tüübispetsifikaatide või konstantide kujul, näeb funktsioonimalli definitsioon välja täpselt sama, mis tavaliste funktsioonide puhul:

malli klass Tüüp, int suurus

/* parameetritega funktsioon otsimiseks

* minimaalne väärtus massiivis */

Tüüp min_val = r_massiiv;

jaoks (int i = 1; i suurus; ++i)

if (r_massiiv[i] min_val)

min_val = r_massiiv[i];

Selles näites määrab tüüp Tüüp funktsiooni min() poolt tagastatava väärtuse tüübi, parameetri r_array tüübi ja kohaliku muutuja min_val tüübi; suurus määrab massiivi r_array suuruse. Programmi töötamise ajal funktsiooni min() kasutamisel mis tahes sisseehitatud ja kasutaja määratletud tüübid ja suuruse asemel - teatud konstantsed avaldised. (Tuletame meelde, et funktsiooniga saab töötada kahel viisil: helistada sellele või võtta selle aadress).

Parameetrite tüüpide ja väärtuste asendamise protsessi nimetatakse malli leidmiseks. (Räägime sellest üksikasjalikumalt järgmises jaotises.)

Funktsiooni min() parameetrite loend võib tunduda väga lühike. Nagu on kirjeldatud jaotises 7.3, kui parameeter on massiiv, edastatakse osuti selle esimesele elemendile, kuid tegeliku massiivi argumendi esimene mõõde funktsiooni määratluses on teadmata. Sellest raskusest ülesaamiseks deklareerisime esimese parameetri väärtuseks min() massiivi viitena ja teise selle suuruseks. Selle lähenemisviisi puuduseks on see, et malli kasutamisel koos massiividega sama tippige int, Aga erinevad suurused genereeritakse (või instantseeritakse) funktsiooni min() erinevad eksemplarid.

Parameetri nime on lubatud kasutada malli deklaratsioonis või definitsioonis. Tüübi parameeter toimib tüübi spetsifikaatorina; seda saab kasutada nagu iga sisseehitatud või kohandatud tüüp, näiteks muutujate deklaratsioonides või tüüpide valamisoperatsioonides. Konstantse väärtusena kasutatakse konstantset parameetrit – kus on vaja konstantseid avaldisi, näiteks massiivi deklaratsioonis suuruse määramiseks või loenduselemendi algväärtusena.

// suurus määrab massiivi parameetri suuruse ja lähtestab

// tüüpi muutuja const int

malli klass Tüüp, int suurus

Tüüp min(konst Tüüp (r_massiiv))

const int loc_size = suurus;

Sisestage loc_array;

Kui malliparameetriga sama nimega objekt, funktsioon või tüüp on deklareeritud globaalses ulatuses, siis globaalne nimi osutub peidetuks. Järgmises näites ei ole tmp muutuja tüüp topelt, vaid sama mis parameetri Type template tüüp:

typedef double Tüüp;

malli klassi tüüp

Tüüp min (tüüp a, tüüp b)

// tmp on sama tüüpi, mis malli parameetril Tüüp, mitte antud

// globaalne typedef

Tüüp tm = a b ? a: b;

Funktsioonimalli definitsioonis deklareeritud objektil või tüübil ei tohi olla sama nime kui ühelgi parameetril:

malli klassi tüüp

Tüüp min (tüüp a, tüüp b)

// viga: nime korduv deklaratsioon Nimele vastav tüüp

// malli parameeter

typedef double Tüüp;

Tüüp tmp = a b ? a: b;

Tagastustüübi määramiseks saab kasutada malli tüübi parameetri nime:

// õige: T1 tähistab väärtuse tüüpi, mille tagastab min(),

// ning T2 ja T3 on selle funktsiooni tüübiparameetrid

malli klass T1, klass T2, klass T3

Ühes parameetrite loendis on teatud nime lubatud kasutada ainult üks kord. Näiteks järgmine definitsioon märgitakse kompileerimisveaks:

// viga: vale taaskasuta parameetri nimi Tüüp

malli klass Tüüp, klass Tüüp

Tüüp min(tüüp, tüüp);

Samas võib malli deklaratsioonis või definitsioonis sama nime kasutada mitu korda:

// õige: tüübi nime uuesti kasutamine malli sees

malli klassi tüüp

Tüüp min(tüüp, tüüp);

malli klassi tüüp

Tüüp max(tüüp, tüüp);

Parameetrite nimed deklaratsioonis ja definitsioonis ei pea ühtima. Seega viitavad kõik kolm min() deklaratsiooni samale funktsioonimallile:

// kõik kolm min() deklaratsiooni viitavad samale funktsioonimallile

// mallideklaratsioonid edasi

malli klass T T min(T, T);

malli klass U U min(U, U);

// tegelik malli definitsioon

malli klassi tüüp

Tüüp min (tüüp a, tüüp b) ( /* ... */ )

Sama malliparameetri kuvamiskordade arv funktsiooni parameetrite loendis on piiramatu. Järgmises näites kasutatakse sõna Tüüp kahe esindamiseks erinevad parameetrid:

// õige: Tüüpi kasutatakse malli parameetrite loendis korduvalt

malli klassi tüüp

Tüüp summa(konst vektortüüp , tüüp);

Kui funktsioonimallil on mitu tüübiparameetrit, peab igale neist eelnema märksõna klassi või tüübi nimi:

// õige: tüübinime ja klassi märksõnu saab vahele panna

malli tüübinimi T, klass U

// viga: peab olema tüübinimi T, klass U või

// tüübinimi T, tüübinimi U

malli tüübinimi T, U

Funktsioonimalli parameetrite loendis on märksõnadel tüübinimi ja klass sama tähendus ja seetõttu on need omavahel asendatavad. Mõlemat neist saab kasutada erinevate mallitüüpide parameetrite deklareerimiseks samas loendis (nagu on näidatud funktsiooni miinus() malliga). Tüübi parameetri tähistamiseks tundub esmapilgul loomulikum kasutada märksõna tüübinimi, mitte klass, sest see näitab selgelt, et sellele järgneb tüübinimi. Kuid see sõna lisati keelde alles hiljuti C++ standardi osana, nii et tõenäoliselt näete sõna klassi vanemates programmides. (Rääkimata sellest, et klass on lühem kui tüübinimi ja inimesed on loomult laisad.)

Tüübinime märksõna muudab mallide definitsioonide sõelumise lihtsamaks. (Põhidalt peatume sellel, miks seda vaja oli. Neil, kes soovivad selle kohta rohkem teada saada, soovitame tutvuda Stroustrupi raamatuga “Design and Evolution of C++”.)

Sel viisil sõelumisel peab kompilaator eristama avaldisi, mis on tüüpilised, mittetüüpilistest avaldistest; seda ei ole alati võimalik tuvastada. Näiteks kui kompilaator kohtab malli definitsioonis avaldist Parm::name ja kui Parm on tüübiparameeter, mis esindab klassi, kas siis peaks eeldama, et nimi esindab klassi Parm tüübiliiget?

malli klass Parm, klass U

Parm::nimi * p; // kas see on osuti deklaratsioon või korrutamine?

// Tegelikult korrutamine

Kompilaator ei tea, kas nimi on tüüp, sest Parmi esindatava klassi definitsioon pole saadaval enne, kui mall on instantseeritud. Sellise malli definitsiooni sõelumiseks peab kasutaja ütlema kompilaatorile, millised avaldised sisaldavad tüüpe. Seda tehakse tüübinime märksõna abil. Näiteks kui tahame, et funktsiooni miinus() mallis avaldis Parm::nimi oleks tüübi nimi ja seetõttu käsitletaks kogu rida osuti deklaratsioonina, siis peame teksti muutma järgmiselt:

malli klass Parm, klass U

Parm miinus (Parm* massiiv, U väärtus)

tüübinimi Parm::nimi * p; // nüüd on see osuti deklaratsioon

Tüübinime märksõna kasutatakse ka malli parameetrite loendis, et näidata, et parameeter on tüüp.

Funktsiooni malli saab deklareerida siseseks või väliseks – täpselt nagu regulaarne funktsioon. Täpsustaja paigutatakse parameetriloendi järele, mitte sõna malli ette.

// õige: täpsustaja parameetrite loendi järel

malli tüübinimi Tüüp

Tüüp min(tüüp, tüüp);

// viga: tekstisisene täpsustaja pole paigas

malli tüübinimi Tüüp

Tüüp min(ArrayType, int);

Harjutus 10.1

Tehke kindlaks, milline neist funktsioonimallide määratlustest on vale. Paranda vead.

a) malli klass T, U, klass V

tühi foo (T, U, V);

b) malli klass T

c) malli klass T1, tüübinimi T2, klass T3

d) tekstisisese malli tüübinimi T

T foo(T, unsigned int*);

(e) malli klass myT, klass myT

void foo(myT, myT);

f) malli klass T

g) typedef char Ctype;

malli klassi tüüp

Ctüüp ​​foo(Ctüüp ​​a, Ctüüp ​​b);

Harjutus 10.2

Millised korduvad mallideklaratsioonid on valed? Miks?

a) malli klassi tüüp

Tüübiriba (tüüp, tüüp);

malli klassi tüüp

Tüübiriba (tüüp, tüüp);

b) malli klass T1, klass T2

tühi riba (T1, T2);

malli tüübinimi C1, tüübinimi C2

tühimik (C1, C2);

Harjutus 10.3

Kirjutage mallina ümber funktsioon putValues() jaotisest 7.3.3. Parameetristage see nii, et mallis oleks kaks parameetrit (massiivi elementide tüübi ja massiivi suuruse jaoks) ja üks funktsiooni parameeter, mis viitab massiivile. Kirjutage funktsioonimalli definitsioon.

Raamatust Microsoft Office autor Leontjev Vitali Petrovitš

Malli valimine Nagu me juba ütlesime, on Publisher loodud töötama “samm-sammult” – me justkui paneme tulevase väljaande tükkhaaval kokku. Ja veelgi täpsemalt loome selle ühe lugematutest mallidest. Publisheri CD sisaldab üle 1500 malli

Raamatust Abijuhend keeles C++ autor Stroustrap Bjarne

R.7.1.4 Tüübimalli spetsifikatsioon Tüübimalli spetsifikatsiooni kasutatakse tüüpide või funktsioonide perekonna määramiseks (vt

Raamatust Tõhus kontoritöö autor Ptašinski Vladimir Sergejevitš

Malli kontseptsioon Tekstide loomise ja vormindamise töö lihtsustamiseks, teksti, graafika paigutuse ja kujunduse standardiseerimiseks, dokumenditöötlustoimingute tüpiseerimiseks ja muuks kasutatakse dokumendimalle. Microsofti pakett Office annab mallile erinevaid määratlusi

Raamatust Database Processing in Visual Basic®.NET autor McManus Geoffrey P

Raamatust Looming Joomla mallid autor autor teadmata

Mallide kataloogi struktuur Nüüd peame hoolitsema mõnede tingimuste eest. Nagu juba mainitud, peab mallil olema kindel kataloogistruktuur: [PathToJoomla!]/templates/[TemplateName]/[PathtoJoomla!]/templates/[Malli nimi]/css/[PathtoJoomla!]/templates/[

XSLT raamatust autor Holzner Stefan

Malli struktuur Lisaks eripealkirjale vajab mall struktuuri. Struktuuri saate luua tabelite või siltide abil

. Järgmisena kirjeldame struktuuri tabeliversiooni loomist. Kui teil on Dremweaveris ikka veel küljendusrežiim lubatud, sulgege

Raamatust XSLT Technology autor Valikov Aleksei Nikolajevitš

Malli loomine 2. peatükis lõin põhimalli planets.xml sõlmede valimiseks ja selle dokumendi teisendamiseks HTML-vormingusse. Stiililehtede mallid luuakse elementide abil , mis määratleb nõutavate teisenduste reeglid. Lõime malli, mis leidis juure

Raamatust The C programming Language for personaalarvuti autor Bochkov S. O.

Malli sisu Tegelikult ei määra mallireeglit määratlev element xsl:template midagi muud kui tingimused, mille korral reeglit tuleks täita. Konkreetsed toimingud ja juhised, mida tuleb täita, on määratud elemendi xsl:template sisuga ja moodustavad

Raamatust The C Language – juhend algajatele autor Prata Steven

Funktsiooni definitsioon Funktsiooni definitsioon määrab funktsiooni nime, formaalsed parameetrid ja põhiosa. See võib määrata ka funktsiooni tagastustüübi ja salvestusklassi. Funktsiooni määratlemise süntaks on:[<спецификация КП>][<спецификация

Raamatust Windows XP dokumentideta ja vähetuntud funktsioonid autor Klimenko Roman Aleksandrovitš

Funktsiooni defineerimine argumendiga: formaalsed argumendid Meie funktsiooni definitsioon algab kahe reaga: space(number)int number Esimene rida teatab kompilaatorile, et funktsioonil space() on argument ja selle nimi on number. Teine rida on kirjeldus, mis näitab

Raamatust Kuidas teha oma veebisaiti ja teenida sellega raha. Praktiline juhend algajatele Internetis raha teenimiseks autor Mukhutdinov Jevgeni

Turvamalli loomine Turvamalli loomiseks mõne muu malli põhjal valige malli kontekstimenüüst käsk Salvesta nimega. Seejärel palub Microsofti halduskonsool teil anda uuele mallile nime, misjärel see kuvatakse

C++ raamatust algajatele autor Lippman Stanley

Autori raamatust

10.2. Funktsioonimalli määramine Funktsioonimall kirjeldab, kuidas konkreetseid funktsioone tuleks konstrueerida, kui neile antakse palju tegelikke tüüpe või väärtusi. Kujundusprotsessi nimetatakse malli instanteerimiseks. Seda teostatakse kaudselt, kõne kõrvalmõjuna

Autori raamatust

Autori raamatust

10.11. Funktsioonimalli näide Selles jaotises on näide, mis näitab, kuidas funktsioonimalle saab määratleda ja kasutada. See määrab sort() malli, mida kasutatakse seejärel massiivi elementide sortimiseks. Massiivi ennast esindab Array klassi mall (vt

Autori raamatust

16.1. Klassimalli määratlemine Oletame, et peame määratlema klassi, mis toetab järjekorramehhanismi. Järjekord on andmestruktuur objektide kogumi salvestamiseks; need asetatakse järjekorra lõppu ja otsitakse algusest peale. Kirjeldatakse järjekorra käitumist

Malli funktsioon määratleb üldise toimingute komplekti, mida rakendatakse erinevat tüüpi andmetele. Seda mehhanismi kasutades on võimalik rakendada mõningaid üldalgoritme paljudele andmetele. Nagu teate, on paljud algoritmid loogiliselt samad, olenemata andmetüübist, millega nad töötavad. Näiteks kiirsortimise algoritm on sama nii täisarvude massiivi kui ka ujukomaarvude massiivi puhul. Erineb ainult sorteeritavate andmete tüüp. Üldfunktsiooni luues saate määrata algoritmi olemuse sõltumata andmetüübist. Pärast seda genereerib kompilaator automaatselt selle andmetüübi jaoks õige koodi, mille jaoks see konkreetne funktsiooni teostus koostamise etapis luuakse. Põhimõtteliselt loob mallifunktsiooni loomisel funktsiooni, mis võib end automaatselt üle koormata.

Malli funktsioonid luuakse malli märksõna abil. Sõna "muster" tavaline tähendus peegeldab üsna täielikult selle kasutamist C++ keeles. Funktsiooni skeleti loomiseks kasutatakse malli, mille rakendamise üksikasjad jäetakse kompilaatori hooleks. Mallfunktsiooni üldvorm on järgmine:

malli return_type funktsiooni_nimi (parameetrite loend)
{
// funktsiooni keha
}

Siin on ptype tüübiparameeter, funktsiooni poolt kasutatava andmetüübi nime "kohahoidja". Seda tüübiparameetrit saab kasutada funktsiooni definitsioonis. Kuid see on ainult "kohahoidja", mille kompilaator asendab funktsiooni konkreetse versiooni loomise ajal automaatselt tegeliku andmetüübiga.

Allpool on lühike näide, mis loob mallifunktsiooni, millel on kaks parameetrit. See funktsioon muudab nende parameetrite väärtusi omavahel. Kuna kahe muutuja vahelise väärtuste vahetamise üldine protsess ei sõltu nende tüübist, saab seda loomulikult rakendada mallifunktsiooni abil.

// funktsioonimalli näide
#kaasa
// funktsiooni mall
malli tühine vahetus (X &a, X &b)
{
X temp;
temp = a;
a = b;
b = temp;
}
int main()
{
int i = 10, j = 20;
ujuki x=10,1, y= 23,3;
char a="x", b="z";
cout<< "Original i, j: " << i << " " << j << endl;
cout<< "Original x, y: " << x << " " << у << endl;
cout<< "Original a, b: " << a << " " << b << endl;
vahetus(i, j); // vahetada täisarvusid
swap(x, y); // reaalsete väärtuste vahetus
swap(a, b); // märgivahetus
cout<< "Swapped i, j: " << i << " " << j << endl;
cout<< "Swapped x, y: " << x << " " << у << endl;
cout<< "Swapped a, b: " << a << " " << b << endl;
tagasi 0;
}

Vaatame seda programmi lähemalt. Liin

Mall tühine vahetus (X &a, X &b)

Näitab kompilaatorile, et malli genereeritakse. Siin on X tüübi parameetrina kasutatav tüübimall. Järgmisena deklareeritakse funktsioon swap(), kasutades andmetüüpi X nende parameetrite jaoks, mis väärtusi vahetavad. Funktsioonis main() kutsutakse funktsiooni swap() välja kolme erinevat tüüpi andmetega: täisarvud, ujukomaarvud ja märgid. Kuna funktsioon swap() on mallifunktsioon, loob kompilaator automaatselt funktsioonist swap() kolm erinevat versiooni – ühe täisarvudega, teise ujukomaarvudega töötamiseks ja lõpuks kolmanda märgimuutujatega töötamiseks.

Õppetund 29. Funktsioonimallide kasutamine

Funktsioonide loomisel tuleb mõnikord ette olukordi, kus kaks funktsiooni teostavad sama töötlust, kuid töötavad erinevatel andmetüüpidel (näiteks üks kasutab parameetreid tüüpi int ja teine ​​tüüpi float). Te teate juba 13. õppetunnist, et funktsioonide ülekoormamise abil saate kasutada sama nime funktsioonide jaoks, mis teevad erinevaid asju ja millel on erinevat tüüpi parameetrid. Kui aga funktsioonid tagastavad erinevat tüüpi väärtusi, peaksite nende jaoks kasutama kordumatuid nimesid (vt 13. õppetüki märkust). Oletame näiteks, et teil on funktsioon nimega max, mis tagastab maksimaalselt kaks täisarvu. Kui vajate hiljem sarnast funktsiooni, mis tagastab maksimaalselt kaks ujukoma väärtust, peaksite defineerima mõne muu funktsiooni, näiteks fmax. Sellest õpetusest saate teada, kuidas kasutada C++ malle, et kiiresti luua eri tüüpi väärtusi tagastavaid funktsioone. Selle õppetunni lõpuks olete omandanud järgmised põhimõisted:

    Mall määratleb lausete komplekti, mida teie programmid saavad hiljem kasutada mitme funktsiooni loomiseks.

    Programmid kasutavad sageli funktsioonimalle, et kiiresti määratleda mitu funktsiooni, mis kasutavad samu avaldusi erinevat tüüpi parameetritega töötamiseks või millel on erinevad tagastustüübid.

    Funktsioonimallidel on konkreetsed nimed, mis vastavad teie programmis kasutatavale funktsiooni nimele.

    Kui teie programm on funktsioonimalli määratlenud, saab see luua konkreetse funktsiooni, kasutades seda malli, et määrata prototüüp, mis sisaldab malli nime, funktsiooni tagastusväärtust ja parameetritüüpe.

    Kompileerimisprotsessi ajal loob C++ kompilaator teie programmis funktsioone, kasutades malli nimele viitavates funktsioonide prototüüpides määratud tüüpe.

Funktsioonimallidel on ainulaadne süntaks, mis ei pruugi esmapilgul selge olla. Kuid pärast ühe või kahe malli loomist leiate, et neid on tegelikult väga lihtne kasutada.

LOOGE LIHTNE FUNKTSIOONIMALL

Funktsioonimall määratleb tüübist sõltumatu funktsiooni. Sellise malli abil saavad teie programmid seejärel määratleda vajalike tüüpidega konkreetseid funktsioone. Näiteks järgmine mall on määratletud funktsioonile nimega max, mis tagastab kahest väärtusest suurema:

malli T max(T a, T b)

(kui (a > b) tagastab (a); muidu tagastab (b); )

T-täht tähistab sel juhul üldist mustritüüpi. Pärast malli määratlemist oma programmis deklareerite funktsioonide prototüübid iga vajaliku tüübi jaoks. Tach-malli puhul loovad järgmised prototüübid funktsioonid tüüpi float ja int.

ujuk max(ujuk, ujuk); int max(int, int);

Kui C++ kompilaator nende prototüüpidega kokku puutub, asendab see malli tüübi T tüübiga, mille määrate funktsiooni koostamisel. Ujuktüübi puhul on max funktsioon pärast asendamist järgmisel kujul:

malli T max(T a, T b)

(kui (a > b) tagastab (a) ; muidu tagastab (b); )

ujuk max(ujuk a, hõljuk b)

(kui (a > b) tagastab (a) ; muidu tagastab (b); )

Järgmine programm MAX_TEMP.CPP kasutab int ja float tüüpi funktsiooni loomiseks max mustrit.

#kaasa

malli T max(T a, T b)

(kui (a > b) tagastab (a); muidu tagastab (b); )

ujuk max(ujuk, ujuk);

int max(int, int);

(välja<< "Максимум 100 и 200 равен " << max(100, 200) << endl; cout << "Максимум 5.4321 и 1.2345 равен " << max(5.4321, 1.2345) << endl; }

Kompileerimise ajal genereerib C++ kompilaator automaatselt laused, et konstrueerida üks funktsioon, mis töötab int-tüübil, ja teine ​​funktsioon, mis töötab ujuktüübiga. Kuna C++ kompilaator haldab mallide abil loodud funktsioonidele vastavaid operaatoreid, võimaldab see kasutada samu nimesid funktsioonide jaoks, mis tagastavad erinevat tüüpi väärtusi. Seda ei saa teha lihtsalt funktsioonide ülekoormamisega, nagu on kirjeldatud 13. õppetükis.

Funktsioonimallide kasutamine

Kuna teie programmid muutuvad keerukamaks, võib juhtuda, et vajate sarnaseid funktsioone, mis täidavad samu toiminguid, kuid erinevat tüüpi andmetüüpidega. Funktsioonimall võimaldab teie programmidel määratleda üldise või tüübist sõltumatu funktsiooni. Kui programm peab kasutama funktsiooni teatud tüübi (nt int või float) puhul, määrab see funktsiooni prototüübi, mis kasutab funktsiooni malli nime ning tagastus- ja parameetritüüpe. Kompileerimise ajal loob C++ vastava funktsiooni. Mallide loomisega vähendate funktsioonide arvu, mida peate ise kodeerima, ja teie programmid saavad kasutada sama nime funktsioonide jaoks, mis täidavad konkreetset toimingut, sõltumata funktsiooni tagastusväärtusest ja parameetritüüpidest.

MITMET TÜÜPI KASUTAVAD MALLID

Eelmine malli definitsioon funktsiooni max jaoks kasutas ühte üldist tüüpi T. Väga sageli peate funktsioonimallis määrama mitu tüüpi. Näiteks järgmised laused loovad malli funktsiooni show_array jaoks, mis kuvab massiivi elemendid. Mall kasutab massiivi tüübi tähistamiseks tüüpi T ja loendusparameetri tüübi tähistamiseks tüüpi T1:

malli

< count; index++) cout << array << " "; cout << endl; }

Nagu varemgi, peab programm määrama vajalike tüüpide funktsioonide prototüübid:

void show_array(int *, int); void show_array(float *, unsigned);

Järgmine programm SHOW_TEM.CPP kasutab malli, et luua funktsioone, mis väljastavad int ja float tüüpi massiive.

#kaasa

malli void show_array(T *massiiv, T1 arv)

( T1 indeks; jaoks (indeks =0; indeks< count; index++) cout << array “ " "; cout << endl; }

void show_array(int *, int);

void show_array(float *, unsigned);

( int pages = ( 100, 200, 300, 400, 500 ); ujuvad hinnadH = ( 10.05, 20.10, 30.15 ); näita_massiiv(lehekülgi, 5); näita_massiiv(hinnad, 3); )

Mallid ja mitut tüüpi

Kuna funktsioonimallid muutuvad keerukamaks, võivad need toetada mitut tüüpi. Näiteks võib teie programm luua malli funktsioonile massiiv_sort, mis sorteerib massiivi elemente. Sel juhul saab funktsioon kasutada kahte parameetrit: esimest, mis vastab massiivile, ja teist, mis vastab massiivi elementide arvule. Kui programm eeldab, et massiiv ei sisalda kunagi rohkem kui 32767 väärtust, võib ta kasutada massiivi suuruse parameetrina int tüüpi. Kuid üldisem muster võib lubada programmil määrata selle parameetri jaoks oma tüübi, nagu allpool näidatud:

malli T , klass T1> void array_sort(T massiiv, T1 elemendid)

( // operaatorid )

Malli array_sort kasutades saab programm luua funktsioone, mis sorteerivad väikseid ujumassiivid (alla 128 elemendi) ja väga suuri int-massiivid, kasutades järgmisi prototüüpe:

void array_sort(float, char); void array_sort(int, long);

MIDA SA PEAD TEADMA

Nagu te juba teate, vähendab funktsioonimallide kasutamine programmeerimiskoormust, võimaldades C++ kompilaatoril genereerida avaldusi funktsioonide jaoks, mis erinevad ainult oma tagastustüüpide ja parameetrite poolest. 30. õppetükis saate teada, kuidas kasutada malle tüübist sõltumatute ehk üldiste klasside loomiseks. Enne 30. õppetüki õppimist veenduge, et olete omandanud järgmised põhimõisted.

      Funktsioonimallid võimaldavad deklareerida tüübist sõltumatuid ehk üldisi funktsioone.

      Kui teie programm peab kasutama teatud andmetüüpidega funktsiooni, peab see määrama funktsiooni prototüübi, mis määrab nõutavad tüübid.

      Kui C++ kompilaator puutub kokku sellise funktsiooni prototüübiga, loob see sellele funktsioonile vastavad avaldused, asendades nõutavad tüübid.

      Teie programmid peavad looma mallid tavapäraste funktsioonide jaoks, mis töötavad erinevatel tüüpidel. Teisisõnu, kui kasutate funktsiooniga ainult ühte tüüpi, pole malli vaja kasutada.

      Kui funktsioon nõuab mitut tüüpi, määrab mall lihtsalt igale tüübile kordumatu identifikaatori, näiteks T, T1 ja T2. Hiljem kompileerimisprotsessis määrab C++ kompilaator õigesti funktsiooni prototüübis määratud tüübid.

Hakkasin kirjutama teksti kõikvõimalikest lahedatest andmestruktuuridest ja siis selgus, et mitmed väga olulised C++ omadused pole meil veel läbi uuritud. Mallid on üks neist.

Mallid on väga võimas tööriist. Mallide funktsioonid ja klassid võivad programmeerija elu oluliselt lihtsustada ning säästa tohutult aega, vaeva ja närve. Kui arvate, et mallid pole eriti oluline teema, mida uurida, siis tea, et eksite.

Malli funktsioonid

Lihtne näide malli funktsioonist:

C++ kood Tüüp ruut (Tüüp a) ( Tüüp b; b = a*a; tagastab b; ) int x = 5; int i; i = ruut(5); ujuki y = 0,5; ujuk f; f = ruut(y);

Kui looksime funktsioonid vanaviisi, siis peaksime kirjutama kaks erinevat funktsiooni: int tüübi ja float tüübi jaoks. Ja kui vajaksite sama funktsiooni teiste tüüpide abil, peaksite selle uuesti kirjutama. Mallide abil saate piirduda vaid ühe funktsiooni eksemplariga, jättes kogu musta töö kompilaatori kanda.

Konkreetse tüübi kasutamise asemel kasutab funktsioon parameetrilist tüüpi (ehk teisisõnu malli argumenti). Siin nimetasin parameetrilist tüüpi identifikaatoriks Tüüp. See identifikaator esineb funktsioonis kolm korda: tagastatav väärtus, funktsiooni argument ja muutuja s definitsioon. See tähendab, et tüüpi kasutatakse nagu iga tavalist tüüpi.

Kuid selleks, et kood töötaks, peate funktsiooni ette lisama järgmise rea (näitasin mitut süntaksivalikut, need kõik töötavad):

C++ kood malli Tippige ruudukujuline (tüüp a) mall < class Type >Tippige ruudukujuline (tüüp a) mall< class Type >Tüüp ruut (tüüp a)

Niisiis, funktsioonile peab eelnema märksõna mall ja nurksulgudes tuleb määrata parameetrilise tüübi nimi koos märksõna klassiga. Klassi märksõna asemel võib kasutada tüüpi – üldiselt pole vahet.

Parameetriline tüübi identifikaator võib samuti olla ükskõik milline. Me kasutame sageli neid: TypeA, TypeB, Datatype, T.

Oluline märkus: mallifunktsioonidel peab olema argument, et kompilaator saaks määrata, millist tüüpi kasutada.

Mallides saab kasutada mitut parameetritüüpi ja loomulikult saab segada parameetrilisi tüüpe tavalistega (peate lihtsalt hoolitsema õige tüübi valamise eest). Toon näite, mis kasutab kahte parameetritüüpi TypeA, TypeB ja baastüüpi int:

C++ kood malli TüüpB näide_funktsioon (tüüp A a, tüüp B b) ( int x = 5; b = a + x; tagastab b; )

Kuid mallifunktsioonid ei ole kõige huvitavam asi, mida me täna vaatame.

Mallklassid

Üldiselt luuakse malliklassid umbes samamoodi nagu mallifunktsioonid – märksõna mall kirjutatakse enne klassi nime. Vaatame malliklasse virna näitel:

C++ kood malli klassi virn ( private: int top; Type s; public: stack (): top(0) () void push(Type var) ( top++; s = var; ) Type pop(); ); malli Tüüp stack::pop() ( Tüüp var = s; top--; return var; ) Siin määratleme

jagas kümnest elemendist koosnevat virna. Need elemendid võivad olla mis tahes tüüpi, täpsemalt allpool.

Ainus, millele tahan teie tähelepanu juhtida, on tõuke- ja popfunktsioonide määratlus. Tõukefunktsioon on määratletud klassi sees ja pop funktsioon väljaspool seda. Kõigi väljaspool klassi deklareeritud funktsioonide jaoks tuleb määrata malli märksõna. Funktsiooni nime ees olev avaldis on sama, mis enne klassi nime.

Nüüd vaatame, kuidas malliklassidega töötada:

C++ kood virna s1; virna s2; s1.push(3); s1.push(2); s1.pop(); s2.push(0,5);

Objekti loomisel tuleb klassi nime järele panna nurksulud, kuhu näidata soovitud tüüp. Pärast seda kasutatakse objekte nii, nagu oleme harjunud.

Malliklassidel on üks hämmastav funktsioon – lisaks standardtüüpidele saavad nad töötada ka kohandatud tüüpidega. Vaatame väikest näidet. Selleks defineerime lihtsa sõdalaste klassi:

C++ kood klassi sõdalane ( avalik: int tervis; sõdalane () : tervis(0) () ); virna s; sõdalane w1; sõdalane w2; sõdalane w3; s.push(w1); s.push(w3); s.pop(); s.push(w2);

Vaata, nüüd saad warrior tüüpi muutujaid virnadesse panna!!! Te ei pruugi mind uskuda, aga see on väga lahe! Näete, kui lahe see on, kui loome loendite põhjal graafikuid ja puid.

Praegu kõik mallide järgi. Hiljem vaatleme malliklasside kasutamise keerukamaid juhtumeid.

Mallide spetsialiseerumine on C++ keele üks "keerulisi" funktsioone ja seda kasutatakse peamiselt teekide loomisel. Kahjuks ei käsitleta selle keele populaarsetes raamatutes mõnda mallide spetsialiseerumise funktsiooni. Veelgi enam, isegi mallidele pühendatud ametliku ISO keelestandardi 53 lehekülge kirjeldavad huvitavaid detaile kaootiliselt, jättes palju enda jaoks „arvama – see on ilmselge”. Lõike all püüdsin selgelt välja tuua mallide spetsialiseerumise põhiprintsiibid ja näidata, kuidas neid põhimõtteid saab kasutada võluloitsude konstrueerimisel.

Tere, Maailm

Kuidas oleme harjunud malle kasutama? Kasutame malli märksõna, seejärel nurksulgudes olevaid nimesid malli parameetrid, millele järgneb tüüp ja nimi. Parameetrite puhul näitavad nad ka, mis see on: tüüp (tüübinimi) või väärtus (näiteks int). Malli enda tüüp võib olla klass (klass), struktuur (struct – tegelikult ka klass) või funktsioon (bool foo() ja nii edasi). Näiteks saab lihtsaima malliklassi "A" määratleda järgmiselt:

Mõne aja pärast tahame, et meie klass töötaks kõigi tüüpide puhul ühtemoodi, kuid mõne keerulise, näiteks int puhul erinevalt. Loll küsimus, kirjutame eriala: see näeb välja sama, mis reklaam, aga valikuid me ei märgi malli nurksulgudes, selle asemel märgime konkreetse argumendid mall selle nime järel:

Mall<>klass A< int >(); // siin int on malli argument
Valmis, saate int jaoks kirjutada spetsiaalse teostuse meetodid ja väljad. Seda eriala nimetatakse tavaliselt täis(täielik spetsialiseerumine või selge spetsialiseerumine). Enamiku praktiliste ülesannete jaoks pole rohkem vaja. Ja kui vaja, siis...

Kohandatud mall on uus mall

Kui loete hoolikalt ISO C++ standardit, leiate huvitava väite: luues spetsiaalse malliklassi, loome uus malli klass(14.5.4.3). Mida see meile annab? Spetsialiseeritud malliklass võib sisaldada meetodeid, välju või tüübideklaratsioone, mida meie spetsialiseerunud malliklassis ei leidu. See on mugav, kui vajate malliklassi meetodit, et töötada ainult konkreetse eriala jaoks - piisab, kui deklareerida meetod ainult sellel erialal, kompilaator teeb ülejäänu:

Spetsiaalsel mallil võivad olla oma malliparameetrid

Kurat, nagu me teame, peitub detailides. See, et spetsialiseeritud malliklass on täiesti uus ja omaette klass, on muidugi huvitav, kuid maagiat on selles vähe. Ja maagia peitub väikeses tagajärjes - kui see on eraldi malliklass, võib sellel olla eraldi malliklass, mis pole kuidagi seotud mittespetsialiseerunud malliklassiga valikuid(parameetrid on nurksulgudes oleva malli järel). Näiteks nii:

Mall< typename S, typename U >klass A< int > {};
Tõsi, see on täpselt see kood, mille koostaja kasutab ei kompileeri- me ei kasuta uusi malli parameetreid S ja U, mis on spetsiaalses malliklassis keelatud (ja spetsialiseerunud kompilaator saab aru, et see on klass, kuna sellel on sama nimi “A” kui juba deklareeritud malliklassil). Kompilaator ütleb isegi spetsiaalse vea: "selgesõnaline spetsialiseerumine kasutab osalise spetsialiseerumise süntaksit, kasutage malli<>selle asemel." Vihjeid, et kui sul pole midagi öelda, siis tuleks kasutada malli<>ja ära näita ennast. Miks saate siis kasutada uusi parameetreid spetsiaalses malliklassis? Vastus on kummaline – selleks, et küsida argumendid erialad (argumendid on klassi nime järel nurksulgudes). See tähendab, et spetsialiseerudes malliklassile, saame lihtsa ja arusaadava sisendi asemel spetsialiseeruda sellele uue valikuid:

Mall< typename S, typename U >klass A< std::map< S, U > > {};
Selline kummaline plaat koostab. Ja kui kasutate saadud malliklassi koos std::map-iga, kasutatakse spetsialiseerumist, kus võtmetüüp std::map on saadaval uue malli S parameetrina ja väärtuse tüüp std::map kui U.

Seda malli spetsialiseerumist, milles määratakse uus parameetrite loend ja nende parameetrite kaudu spetsialiseerumise argumendid, nimetatakse osaline spetsialiseerumine(osaline spetsialiseerumine). Miks "osaline"? Ilmselt seetõttu, et see oli algselt mõeldud süntaksiks malli spetsialiseerimiseks mitte kõigi argumentide jaoks. Näide, kus kahe parameetriga malliklass on spetsialiseerunud ainult ühele neist (spetsialiseerumine töötab siis, kui esimene argument T on määratud kui int. Sel juhul võib teine ​​argument olla ükskõik milline – selleks on uus parameeter U kasutusele osalises spetsialiseerumises ja täpsustatud spetsialiseerumise argumentide loendis):

Mall< typename T, typename S >klass B(); malli< typename U >klass B< int, U > {};

Osalise spetsialiseerumise maagilised tagajärjed

Eespool kirjeldatud mallide spetsialiseerumise kahel omadusel on mitmeid huvitavaid tagajärgi. Näiteks osalise spetsialiseerumise kasutamisel saate uusi malliparameetreid tutvustades ja nende kaudu spetsiifilisi argumente kirjeldades jagada keerulised tüübid lihtsateks. Allolevas näites kasutatakse spetsiaalset malliklassi A, kui malli argumendiks on funktsiooni osuti tüüp. Sel juhul saate uute malliparameetrite S ja U kaudu saada selle funktsiooni tagastusväärtuse tüübi ja selle argumendi tüübi:

Mall< typename S, typename U >klass A< S(*)(U) > {};
Ja kui deklareerite typedefi või staatilise const int spetsiaalses mallis (kasutades ära asjaolu, et tegemist on uue malliga), saate seda kasutada tüübist vajaliku teabe eraldamiseks. Näiteks kasutame objektide salvestamiseks malliklassi ja tahame saada läbitud objekti suurust või 0, kui see on osuti. Kahes reas:

Mall< typename T >struct Get ( const static int Size = sizeof(T); ); malli< typename S >struct Get< S* >( const static int Suurus = 0; ); Hangi< int >::Suurus // näiteks 4 Get< int* >::Suurus // 0 - kursor leitud :)
Seda tüüpi maagiat kasutatakse peamiselt raamatukogudes: stl, boost, loki ja nii edasi. Muidugi on selliste nippide kasutamine kõrgetasemelises programmeerimises veidi keeruline - vist kõik mäletavad massiivi suuruse saamise konstruktsiooni :). Kuid raamatukogudes on osalise spetsialiseerumisega suhteliselt lihtne juurutada delegaate, üritusi, keerulisi konteinereid ja muud mõnikord väga vajalikku ja kasulikku.

Kolleegid, kui leiate vea (ja mina kahjuks ei ole guru – võin eksida) või teil on eelnevale kriitikat, küsimusi või täiendusi, siis võtan hea meelega kommentaare.

Värskendus: lubatud jätkamine