Deduceți tipul de returnare al unei funcții șablon. Șabloane

10.1. Definirea unui șablon de funcție

Uneori poate părea că un limbaj puternic tipizat face dificilă implementarea a ceva. funcții simple. De exemplu, desi următorul algoritm Funcția min() este trivială, tastarea puternică necesită implementarea unor variații ale acesteia pentru toate tipurile pe care le vom compara:

int min(int a, int b) (

returnează un b? a: b;

dublu min(dublu a, dublu b) (

returnează un b? a: b;

O alternativă tentantă la definirea explicită a fiecărei instanțe a funcției min() este utilizarea macrocomenzilor extensibile de preprocesor:

#definiți min(a, b) ((a) (b) ? (a) : (b))

Dar această abordare este plină de potențiale pericole. Macrocomanda definită mai sus funcționează corect când cereri simple la min(), de exemplu:

min(10,0, 20,0);

dar poate aduce surprize în cazuri mai complexe: un astfel de mecanism nu se comportă ca un apel de funcție, efectuează doar înlocuirea argumentului textual. Ca rezultat, valorile ambelor argumente sunt evaluate de două ori: o dată când se compară a și b și o dată când se calculează rezultatul returnat de macro:

#include iostream

#definiți min(a,b) ((a) (b) ? (a) : (b))

const int dimensiune = 10;

în timp ce (min(p++,ia) != ia)

cout "elem_cnt: "elem_cnt

" așteptând: " dimensiune endl;

La prima vedere, acest program numără numărul de elemente dintr-o matrice ia de numere întregi. Dar în acest caz, macro-ul min() se extinde incorect deoarece operația de post-increment este aplicată argumentului pointer de două ori pentru fiecare substituție. Ca rezultat, programul tipărește o linie care indică calcule incorecte:

elem_cnt: 5 așteptând: 10

Șabloanele de funcție oferă un mecanism prin care putem păstra semantica definițiilor și apelurilor de funcții (încapsularea unei bucăți de cod într-un singur loc în program și asigurându-ne că argumentele sunt evaluate o singură dată) fără a sacrifica tastarea puternică a limbajului C++, așa cum este cazul macro-urilor.

Șablonul oferă algoritmul folosit pentru generare automată instanțele de funcție cu tipuri variate. Programatorul parametriază toate sau doar unele dintre tipurile din interfața funcției (adică tipurile de parametri formali și de valori returnate), lăsând corpul acestuia neschimbat. O funcție este potrivită pentru a fi un șablon dacă implementarea sa rămâne invariabilă într-un anumit set de instanțe care diferă în tipuri de date, cum ar fi, de exemplu, în cazul min().

Iată cum este definit șablonul funcției min():

Clasa șablon Tip

Tip min2(Tip a, tip b) (

returnează un b? a: b;

// corect: min(int, int);

// corect: min(dublu, dublu);

min(10,0, 20,0);

Dacă în locul macro-ului preprocesor min() înlocuim acest șablon în textul programului anterior, rezultatul va fi corect:

elem_cnt: 10 așteptând: 10

(ÎN bibliotecă standard C++ are șabloane de funcții pentru mulți algoritmi utilizați în mod obișnuit, cum ar fi min(). Acești algoritmi sunt descriși în Capitolul 12. Și în acest capitol introductiv prezentăm propriile noastre versiuni simplificate ale unora dintre algoritmii din biblioteca standard.)

Atât declarația, cât și definirea unui șablon de funcție trebuie să înceapă întotdeauna cu șablonul de cuvinte cheie, urmate de o listă de identificatori, separate prin virgulă, încadrate între paranteze unghiulare „ și „ – o listă de parametri ai șablonului, care nu trebuie să fie goală. Un șablon poate avea parametri de tip, care reprezintă un tip, și parametri constanți, care reprezintă o expresie constantă fixă.

Parametrul tip constă din cuvântul cheie class sau cuvântul cheie typename urmat de un identificator. Aceste cuvinte indică întotdeauna că următorul nume se referă la un tip încorporat sau definit de utilizator. Numele parametrului șablon este ales de programator. În exemplul de mai sus, am folosit numele Type, dar am fi putut alege orice altceva:

clasa șablon Glorp

Glorp min2(Glorp a, Glorp b) (

returnează un b? a: b;

La instanțierea (crearea unei instanțe specifice) a unui șablon, tipul real încorporat sau definit de utilizator este înlocuit cu parametrul tip. Oricare dintre tipurile int, double, char*, vectorint sau listdouble este un argument de șablon valid.

Parametrul constant arată ca anunț obișnuit. Se spune că, în loc de numele parametrului, ar trebui înlocuită valoarea constantă din definiția șablonului. De exemplu, dimensiunea este un parametru constant care reprezintă dimensiunea matricei arr:

clasă șablon Tip, dimensiune int

Tastați min(Tip (arr));

Urmează lista de parametri șablon este o declarație sau o definiție a funcției. Dacă nu acordați atenție prezenței parametrilor sub formă de specificatori de tip sau constante, atunci definiția unui șablon de funcție arată exact la fel ca pentru funcțiile obișnuite:

clasă șablon Tip, dimensiune int

/* functie parametrizata pentru cautare

* valoarea minimaîn matrice */

Tastați min_val = r_array;

pentru (int i = 1; dimensiunea i; ++i)

if (r_array[i] min_val)

min_val = r_array[i];

In aceea exemplu Tip determină tipul valorii returnate de funcția min(), tipul parametrului r_array și tipul variabilei locale min_val; size specifică dimensiunea matricei r_array. În timpul funcționării programului, când se utilizează funcția min(), orice încorporat și definit de utilizator tipuri, iar în loc de mărime - anumite expresii constante. (Amintiți-vă că puteți lucra cu o funcție în două moduri: sunați-o sau luați-i adresa).

Procesul de înlocuire a tipurilor și valorilor cu parametrii se numește instanțiere de șablon. (Vom intra în mai multe detalii despre aceasta în secțiunea următoare.)

Lista de parametri pentru funcția noastră min() poate părea foarte scurtă. După cum sa discutat în Secțiunea 7.3, atunci când parametrul este o matrice, este trecut un pointer către primul său element, dar prima dimensiune a argumentului matricei efective din definiția funcției este necunoscută. Pentru a ocoli această dificultate, am declarat primul parametru la min() ca referință la matrice, iar al doilea ca dimensiune. Dezavantajul acestei abordări este că atunci când se utilizează un model cu matrice de același tip int, dar marimi diferite sunt generate (sau instanțiate) diferite instanțe ale funcției min().

Numele parametrului poate fi utilizat într-o declarație sau definiție șablon. Parametrul de tip servește ca un specificator de tip; poate fi folosit la fel ca orice încorporat sau tip personalizat, de exemplu în declarații de variabile sau operațiuni de turnare de tip. Un parametru constant este utilizat ca valoare constantă - unde sunt necesare expresii constante, de exemplu pentru a specifica dimensiunea într-o declarație de matrice sau ca valoarea initiala element de enumerare.

// dimensiunea determină dimensiunea parametrului matricei și inițializează

// variabilă de tip const int

clasă șablon Tip, dimensiune int

Tastați min(const Tip (r_array))

const int loc_size = dimensiune;

Tastați loc_array;

Dacă un obiect, o funcție sau un tip cu același nume ca un parametru șablon este declarat în domeniul global, numele global este ascuns. În exemplul următor, tipul variabilei tmp nu este dublu, ci este același cu parametrul tip șablon:

typedef dublu Tip;

Clasa șablon Tip

Tip min (Tip a, Tip b)

// tmp este de același tip ca parametrul de șablon Tip, nu cel dat

// global typedef

Tip tm = a b ? a: b;

Un obiect sau un tip declarat în interiorul unei definiții de șablon de funcție nu poate avea același nume ca oricare dintre parametrii:

Clasa șablon Tip

Tip min (Tip a, Tip b)

// eroare: declararea repetată a numelui Tip care se potrivește cu numele

// parametru șablon

typedef dublu Tip;

Tastați tmp = a b ? a: b;

Numele parametrului tip șablon poate fi folosit pentru a specifica tipul de returnare:

// corect: T1 reprezintă tipul de valoare returnat de min(),

// și T2 și T3 sunt parametrii de tip ai acestei funcții

șablon clasa T1, clasa T2, clasa T3

Într-o listă de parametri, un anumit nume poate fi folosit o singură dată. De exemplu, următoarea definiție ar fi semnalată ca o eroare de compilare:

// eroare: incorect reutilizare numele parametrului Tip

tip de clasă șablon, tip de clasă

Tastați min(Tip, Tip);

Cu toate acestea, același nume poate fi folosit de mai multe ori într-o declarație sau definiție șablon:

// corect: reutilizarea numelui Type în interiorul șablonului

Clasa șablon Tip

Tastați min(Tip, Tip);

Clasa șablon Tip

Type max(Tip, Tip);

Numele parametrilor din declarație și definiție nu trebuie să se potrivească. Astfel, toate cele trei declarații min() se referă la același șablon de funcție:

// toate cele trei declarații min() se referă la același șablon de funcție

// transmite declarații șablon

clasă șablon T T min(T, T);

șablon clasa U U min(U, U);

// definiția actuală a șablonului

Clasa șablon Tip

Tastați min(Tip a, Tip b) ( /* ... */ )

Numărul de ori când același parametru șablon apare în lista parametrilor funcției este nelimitat. Următorul exemplu folosește Type pentru a reprezenta două parametri diferiți:

// corect: Type este folosit în mod repetat în lista parametrilor șablonului

Clasa șablon Tip

Tip sum(const vectorType , Type);

Dacă un șablon de funcție are mai mulți parametri de tip, atunci fiecare dintre aceștia trebuie precedat de cuvânt cheie clasa sau nume de tip:

// corect: tipul și cuvintele cheie de clasă pot fi intercalate

Nume tip șablon T, clasa U

// eroare: trebuie să fie tipul T, clasa U sau

// nume de tip T, nume de tip U

Nume tip șablon T, U

În lista de parametri a unui șablon de funcție, cuvintele cheie typename și class au același sens și, prin urmare, sunt interschimbabile. Oricare dintre aceștia poate fi folosit pentru a declara diferiți parametri de tip șablon în aceeași listă (după cum este demonstrat cu șablonul funcției minus()). Pentru a desemna un parametru de tip, este mai firesc, la prima vedere, să folosiți cuvântul cheie typename mai degrabă decât class, deoarece indică clar că este urmat de un nume de tip. Cu toate acestea, acest cuvânt a fost adăugat recent în limbaj ca parte a standardului C++, așa că probabil veți vedea clasa de cuvinte în programele mai vechi. (Ca să nu mai vorbim că clasa este mai scurtă decât numele tipului, iar oamenii sunt leneși din fire.)

Cuvântul cheie typename facilitează analizarea definițiilor șablonului. (Ne vom opri doar pe scurt asupra motivului pentru care a fost necesar. Pentru cei care doresc să afle mai multe despre acest lucru, vă recomandăm să consultați cartea lui Stroustrup „Design and Evolution of C++”).

Când parsează în acest fel, compilatorul trebuie să distingă expresiile care sunt tipuri de cele care nu sunt; nu este întotdeauna posibil să identificăm acest lucru. De exemplu, dacă compilatorul întâlnește expresia Parm::name într-o definiție de șablon și dacă Parm este un parametru de tip care reprezintă o clasă, ar trebui să se presupune că numele reprezintă un membru de tip al clasei Parm?

șablon clasa Parm, clasa U

Parm::nume * p; // este aceasta o declarație de indicator sau o înmulțire?

// De fapt înmulțire

Compilatorul nu știe dacă name este un tip, deoarece definiția clasei reprezentate de Parm nu este disponibilă până când șablonul este instanțiat. Pentru ca o astfel de definiție de șablon să fie analizată, utilizatorul trebuie să spună compilatorului care expresii includ tipurile. Acest lucru se face folosind cuvântul cheie typename. De exemplu, dacă dorim ca expresia Parm::name din șablonul funcției minus() să fie numele unui tip și, prin urmare, întreaga linie să fie tratată ca o declarație de indicator, atunci trebuie să modificăm textul după cum urmează:

șablon clasa Parm, clasa U

Parm minus (matrice Parm*, valoare U)

tipnume Parm::nume * p; // acum aceasta este o declarație de pointer

Cuvântul cheie typename este, de asemenea, utilizat într-o listă de parametri șablon pentru a indica faptul că parametrul este un tip.

Un șablon de funcție poate fi declarat ca inline sau extern, la fel ca o funcție obișnuită. Specificatorul este plasat după lista de parametri, nu înaintea șablonului de cuvinte.

// corect: specificatorul după lista de parametri

nume tip șablon Tip

Tastați min(Tip, Tip);

// eroare: specificatorul inline nu este în vigoare

nume tip șablon Tip

Tastați min(ArrayType, int);

Exercițiul 10.1

Determinați care dintre aceste definiții de șablon de funcție este incorectă. Corectați greșelile.

(a) șablon clasa T, U, clasa V

void foo(T, U, V);

(b) șablon clasa T

(c) șablon clasa T1, nume tip T2, clasa T3

(d) nume de tip șablon inline T

T foo(T, unsigned int*);

(e) șablon clasa myT, clasa myT

void foo(myT, myT);

(f) șablon clasa T

(g) typedef char Ctype;

Clasa șablon Tip

Ctype foo(Ctype a, Ctype b);

Exercițiul 10.2

Care dintre declarațiile șablon repetate sunt greșite? De ce?

(a) tip de clasă șablon

Bara de tip (Tip, Tip);

Clasa șablon Tip

Bara de tip (Tip, Tip);

(b) șablon clasa T1, clasa T2

bară goală (T1, T2);

tip de șablon C1, tip C2

bară goală (C1, C2);

Exercițiul 10.3

Rescrieți funcția putValues() din Secțiunea 7.3.3 ca șablon. Parametrizați-l astfel încât să existe doi parametri șablon (pentru tipul de elemente ale matricei și pentru dimensiunea matricei) și un parametru de funcție care este o referință la matrice. Scrieți o definiție a unui șablon de funcție.

Din cartea Microsoft Office autor Leontiev Vitali Petrovici

Alegerea unui șablon După cum am spus deja, Publisher este proiectat să funcționeze într-un mod „pas cu pas” -, parcă, alcătuim o viitoare publicație bucată cu piesă. Și chiar mai precis, îl creăm pe baza unuia dintre nenumăratele șabloane. Pe CD-ul Publisher sunt stocate peste o mie și jumătate de șabloane

Din carte Ghid de ajutorîn C++ autor Stroustrap Bjarne

R.7.1.4 Specificația tip șablon O specificație tip șablon este utilizată pentru a specifica o familie de tipuri sau funcții (vezi

Din cartea Munca eficientă de birou autor Ptaşinski Vladimir Sergheevici

Conceptul de șablon Pentru a simplifica munca de creare și formatare a textelor, pentru a standardiza aranjarea și proiectarea textului, a graficelor, a tipifica operațiunile de procesare a documentelor și alte lucruri, se folosesc șabloane de documente. Pachetul Microsoft Biroul dă diverse definițiișablon

Din cartea Procesarea bazei de date în Visual Basic®.NET autor McManus Geoffrey P

Din cartea Creația Șabloane Joomla autor autor necunoscut

Structura directorului șablonului Acum trebuie să ne ocupăm de anumite condiții. După cum sa menționat deja, șablonul trebuie să aibă o anumită structură de director: [PathToJoomla!]/templates/[TemplateName]/[PathToJoomla!]/templates/[TemplateName]/css/[PathToJoomla!]/templates/[

Din cartea XSLT autor Holzner Stephen

Structura șablonului Pe lângă un titlu special, un șablon are nevoie de structură. Puteți crea o structură folosind tabele sau etichete

. În continuare, descriem crearea unei versiuni tabelare a structurii. Dacă încă aveți modul Layout activat în Dremweaver, închideți

Din cartea XSLT Technology autor Valikov Alexey Nikolaevici

Crearea unui șablon În capitolul 2, am creat un șablon de bază pentru a selecta nodurile în planets.xml și a converti acel document în HTML. Șabloanele din foile de stil sunt create folosind elemente , definind regulile pentru transformarile necesare. Am creat un șablon care a găsit rădăcina

Din cartea Limbajul de programare C pentru calculator personal autorul Bochkov S. O.

Corpul șablonului De fapt, elementul xsl:template care definește o regulă șablon nu specifică nimic mai mult decât condițiile în care regula ar trebui să fie executată. Acțiunile și instrucțiunile specifice care trebuie executate sunt determinate de conținutul elementului xsl:template și constituie

Din cartea The C Language - A Guide for Beginners de Prata Steven

Definiția funcției O definiție a funcției specifică numele, parametrii formali și corpul funcției. De asemenea, poate specifica tipul de returnare a funcției și clasa de stocare. Sintaxa pentru definirea unei funcții este:[<спецификация КП>][<спецификация

Din cartea Caracteristici nedocumentate și puțin cunoscute ale Windows XP autor Klimenko Roman Alexandrovici

Definirea unei funcții cu un argument: argumente formale Definiția funcției noastre începe cu două linii: space(number)int number Prima linie informează compilatorul că funcția space() are un argument și că numele ei este număr; Al doilea rând este o descriere care indică

Din cartea Cum să-ți faci propriul site web și să faci bani pe el. Un ghid practic pentru începători despre a face bani online autor Mukhutdinov Evgheni

Crearea unui șablon de securitate Pentru a crea un șablon de securitate bazat pe orice alt șablon, selectați comanda Salvare ca din meniul contextual al șablonului. Consola de management Microsoft vă va solicita apoi să furnizați un nume pentru noul șablon, după care va apărea în

Din cartea C++ pentru începători de Lippman Stanley

Din cartea autorului

10.2. Specificarea unui șablon de funcție Un șablon de funcție descrie modul în care anumite funcții ar trebui să fie construite atunci când sunt date mai multe tipuri sau valori reale. Procesul de proiectare se numește instanțiere șablon. Se execută implicit, ca efect secundar al apelului

Din cartea autorului

Din cartea autorului

10.11. Exemplu de șablon de funcție Această secțiune oferă un exemplu care arată cum pot fi definite și utilizate șabloanele de funcție. Aceasta definește șablonul sort(), care este apoi folosit pentru a sorta elementele matricei. Matricea în sine este reprezentată de șablonul de clasă Array (vezi

Din cartea autorului

16.1. Definirea unui șablon de clasă Să presupunem că trebuie să definim o clasă care acceptă un mecanism de așteptare. O coadă este o structură de date pentru stocarea unei colecții de obiecte; sunt plasate la sfârșitul cozii și recuperate de la început. Este descris comportamentul cozii

Puteți crea prototipul unui șablon de funcție declarându-l în prealabil. Această declarație informează compilatorul despre prezența șablonului și, de asemenea, informează compilatorul despre parametrii așteptați. De exemplu, prototipul șablonului funcției de sortare ar arăta astfel:

șablon void Sort (matrice T, dimensiune Tsize);

Este posibil ca numele parametrilor formali ai unui șablon să nu fie aceleași în predeclararea și definirea șablonului. Deci, de exemplu, în următorul fragment, atât prototipul șablonului, cât și definiția șablonului se referă la aceeași funcție:

șablon T max(T, T);

șablon

Tip max(Tip a, tip b)

dacă (a > b) returnează a; altfel returnează b;

Utilizarea unui șablon de funcție

Un șablon de funcție descrie modul în care o anumită funcție poate fi construită pe baza unuia sau mai multor tipuri reale. Compilatorul creează automat un reprezentant al corpului de funcție pentru tipurile specificate în apel. Acest proces se numește specificație. Apare atunci când este apelată o funcție șablon.

De exemplu, în exemplul următor, funcția min() este instanțiată de două ori: o dată cu tipul int și o dată cu tipul double:

șablon

Tastați min (Tip a, Tip b)

în cazul în care o< b) return a; else return b;

int x = 4, y = 5, z;

dublu t = 6,56, r = 3,07, p;

Specializare șablon de funcție

O funcție de șablon specializată este o funcție obișnuită al cărei nume este același cu cel al funcției din șablon, dar care este definită pentru parametri de anumite tipuri. Funcțiile de șablon specializate sunt definite atunci când un șablon generic nu este potrivit pentru un anumit tip de date. De exemplu, funcția șablon min

șablon

Tastați min (Tip a, Tip b)

în cazul în care o< b) return a; else return b;

nu poate fi folosit pentru șiruri de caractere (pentru tipul char*), deoarece codul generat de compilator va compara pur și simplu pozițiile lor de memorie (adresele). Pentru a compara corect șirurile de caractere, puteți defini o funcție specializată:

char* min(char* s1, char* s2)

dacă (strcmp(s1,s2)>0) returnează s2; altfel returnează s1;

Apoi puteți accesa o astfel de funcție în același mod ca o funcție șablon:

int i1 = 3, i2 = 5;

cout<< “max int = ” << max(i1, i2) << endl;

char* s1 = „Vulturul de Aur”;

char* s2 = „Șoim perigrin”;

cout<< “max str = “ << max(s1, s2) << endl;

Șabloane de clasă

Șablon de clasă oferă o definiție generală a unei familii de clase folosind tipuri sau constante arbitrare. Modelul definește membri de date șablonȘi funcții de membru șablon. Odată ce un șablon de clasă a fost definit, puteți instrui compilatorului să genereze o nouă clasă pe baza acestuia pentru un anumit tip sau constantă.

Sintaxa șablonului de clasă

șablon<<список аргументов шаблона>>

clasă<имя класса>

<тело класса>

Cuvântul cheie șablon este urmat de unul sau mai multe argumente (parametri) cuprinse între paranteze unghiulare și separate prin virgule. Fiecare argument poate fi un cuvânt cheie de clasă urmat de un identificator care indică tipul parametrizat. Lista de argumente șablon nu poate fi goală.

Urmează definiția clasei. Este similar cu o definiție de clasă obișnuită, cu excepția faptului că folosește o listă de argumente șablon.

Parametrii șablonului constând din cuvântul cheie de clasă urmat de un identificator sunt adesea numiți parametrii de tip. Cu alte cuvinte, ei informează compilatorul că șablonul așteaptă un tip ca argument.

Am considerat deja un astfel de instrument ca șabloane în C++ când am creat . De ce ar trebui să folosiți șabloane a fost scris în articolul cu șabloane de funcție. Acolo ne-am uitat la prevederile de bază ale șabloanelor în C++. Să le amintim.

Orice șablon începe cu șablonul cuvânt, fie că este un șablon de funcție sau un șablon de clasă. Cuvântul cheie șablon este urmat de paranteze unghiulare −< >, care listează o listă de parametri de șablon. Fiecare parametru trebuie să fie precedat de clasa sau tipul de cuvânt rezervat. Absența acestor cuvinte cheie va fi interpretată de compilator ca . Câteva exemple de declarații șablon:

Șablon

Șablon

Șablon

Cuvântul cheie typename îi spune compilatorului că șablonul va folosi un tip de date încorporat, cum ar fi: int , double , float , char , etc. Și cuvântul cheie class îi spune compilatorului că șablonul de funcție va folosi tipuri de date personalizate ca parametru , adică cursuri. Dar sub nicio formă nu confundați un parametru șablon cu un șablon de clasă. Dacă trebuie să creăm un șablon de clasă cu un parametru de tip int și char , șablonul de clasă va arăta astfel:

Șablon

unde T este un parametru șablon de clasă care poate accepta oricare dintre tipurile de date încorporate, care este ceea ce avem nevoie.

Și dacă parametrul șablonului de clasă trebuie să fie de tip personalizat, cum ar fi Array , unde Array este o clasă care descrie o matrice, șablonul de clasă va arăta astfel:

Șablon Nume clasa (//corp șablon de clasă);

Este mai bine să vă dați seama de acest lucru de la început, astfel încât să nu apară erori mai târziu, chiar dacă șablonul de clasă este scris corect.

Să creăm un șablon de clasă Stack, unde , care stochează elemente de date similare. Puteți împinge și introduce datele în stivă. Un element adăugat stivei este plasat în partea de sus a stivei. Elementele stivei sunt îndepărtate începând de sus. În șablonul de clasă Stack, trebuie să creați principalele metode:

  • Apăsaţi— adăugați un element la stivă;
  • Pop- scoateți un element din stivă
  • printStack— afișarea stivei pe ecran;

Deci, să implementăm aceste trei metode și, în final, vom obține cea mai simplă clasă care implementează funcționarea structurii stivei. Nu uitați de constructori și destructori. Vezi codul de mai jos.

#include „stdafx.h” #include șablon << "Заталкиваем элементы в стек: "; int ct = 0; while (ct++ != 5) { int temp; cin >> << "\nУдаляем два элемента из стека:\n"; myStack.pop(); // удаляем элемент из стека myStack.pop(); // удаляем элемент из стека myStack.printStack(); // вывод стека на экран return 0; } // конструктор template Grămadă ::Stiva(int s) ( dimensiune = s > Grămadă bool Stiva bool Stiva void Stack ::printStack() ( pentru (int ix = dimensiunea -1; ix >= 0; ix--) cout<< "|" << setw(4) << stackPtr << endl; }

// cod Cod::Blocuri

// Cod Dev-C++

#include folosind namespace std; #include șablon class Stack (privat: T *stackPtr; // pointer la dimensiunea stivei int; // dimensiunea stivei T top; // partea de sus a stivei public: Stack(int = 10); // implicit dimensiunea stivei este de 10 elemente ~Stack() ; // destructor bool push(const T // împinge un element în stiva bool pop()); int main() (Stiva myStack(5); // umple stiva<< "Заталкиваем элементы в стек: "; int ct = 0; while (ct++ != 5) { int temp; cin >> temp; myStack.push(temp); ) myStack.printStack(); // imprimă stiva pe ecranul<< "\nУдаляем два элемента из стека:\n"; myStack.pop(); // удаляем элемент из стека myStack.pop(); // удаляем элемент из стека myStack.printStack(); // вывод стека на экран return 0; } // конструктор template Grămadă ::Stack(int s) ( size = s > 0 ? s: 10; // inițializați dimensiunea stivei stackPtr = new T; // alocați memorie pentru partea de sus a stivei = -1; // valoarea -1 indică faptul că stiva gol ) // destructor de șablon Grămadă ::~Stack() ( șterge stackPtr; // șterge stiva ) // elementul este o funcție a clasei Stack pentru plasarea unui element pe stivă // valoarea returnată este adevărată, operația finalizată cu succes // fals, elementul nu a fost adăugat la șablonul de stivă bool Stiva ::push(const T value) ( ​​​​dacă (sus == dimensiune - 1) returnează fals; // stiva este plină top++; stackPtr = valoare; // împinge elementul în stivă returnează adevărat; // finalizare cu succes a operației ) // funcția element Clasa de stivă pentru a elimina un element din stivă // valoarea returnată este adevărată, operația s-a finalizat cu succes // fals, stiva este șablonul gol bool Stiva ::pop() (dacă (sus == - 1) returnează fals; // stiva este goală stackPtr = 0; // elimină un element din partea de sus a stivei--; returnează adevărat; // finalizarea cu succes a operației) // afișează stiva pe șablonul de ecran void Stack ::printStack() ( pentru (int ix = dimensiunea -1; ix >= 0; ix--) cout<< "|" << setw(4) << stackPtr << endl; }

După cum puteți vedea, șablonul de clasă Stack este declarat și definit în fișierul cu funcția principală. Desigur, această metodă de reciclare a șabloanelor nu este bună, dar va funcționa ca exemplu. Rândurile 7 - 20 declară interfața șablonului de clasă. Declarația de clasă se face în mod obișnuit, iar înaintea clasei există o declarație șablon, pe linia 7. Când declarați un șablon de clasă, folosiți întotdeauna această sintaxă.

Rândurile 47 - 100 conțin elementul de funcție șablon al clasei Stack, iar înainte de fiecare funcție este necesar să se declare un șablon, exact la fel ca înainte de clasă - șablon . Adică, se dovedește că elementul funcție al unui șablon de clasă este declarat exact în același mod ca șabloanele de funcție obișnuite. Dacă am descrie implementarea metodelor în interiorul unei clase, atunci antetul șablonului ar fi șablon Nu este nevoie să vă înregistrați pentru fiecare funcție.

Pentru a lega fiecare element de funcție la un șablon de clasă, ca de obicei, folosim operația de rezoluție a domeniului binar - :: cu numele șablonului de clasă - Stivă . Ceea ce am făcut în rândurile 49, 58, 68, 83, 96.

Notați declarația obiectului myStack al șablonului de clasă Stack în funcția principală, linia 24. Parantezele unghiulare trebuie să indice în mod explicit tipul de date utilizat, acest lucru nu a fost necesar în șabloanele de funcție. În continuare, main rulează câteva funcții care demonstrează cum funcționează șablonul de clasă Stack. Rezultatul programului este prezentat mai jos.

Împingem elemente pe stivă: 12 3456 768 5 4564 |4564 | 5 | 768 |3456 | 12 Scoateți două elemente din stivă: | 0 | 0 | 768 |3456 | 12

Șabloanele de funcție, cu alte cuvinte, sunt instrucțiuni conform cărora versiunile locale ale unei funcții șablon sunt create pentru un set specific de parametri și tipuri de date.

De fapt, șabloanele de funcții sunt un instrument puternic în C++ care ușurează mult munca unui programator. De exemplu, trebuie să programăm o funcție care să afișeze elementele unui tablou. Sarcina nu este dificilă! Dar pentru a scrie o astfel de funcție, trebuie să cunoaștem tipul de date al matricei pe care îl vom afișa pe ecran. Și apoi ne spun - există mai mult de un tip de date, dorim ca funcția să scoată matrice de tipuri int, double, float și char.

După cum sa dovedit, sarcina a devenit mai complicată. Și acum înțelegem că trebuie să programăm până la 4 funcții care efectuează aceleași acțiuni, dar pentru diferite tipuri de date. Deoarece nu suntem încă familiarizați cu șabloanele de funcții, vom face acest lucru: vom folosi .

// supraîncărcarea funcției printArray pentru a afișa matricea pe ecran void printArray(const int * array, int count) ( for (int ix = 0; ix< count; ix++) cout << array << " "; cout << endl; } void printArray(const double * array, int count) { for (int ix = 0; ix < count; ix++) cout << array << " "; cout << endl; } void printArray(const float * array, int count) { for (int ix = 0; ix < count; ix++) cout << array << " "; cout << endl; } void printArray(const char * array, int count) { for (int ix = 0; ix < count; ix++) cout << array << " "; cout << endl; }

Astfel, avem 4 funcții supraîncărcate, pentru diferite tipuri de date. După cum puteți vedea, ele diferă doar prin antetul funcției, corpul lor este absolut același. Am scris corpul funcției o dată pentru tipul int și l-am copiat de trei ori pentru alte tipuri de date.

Și, dacă rulați programul cu aceste funcții, acesta va funcționa corect. Compilatorul însuși va determina ce funcție să folosească la apelare.

După cum puteți vedea, a existat destul de mult cod pentru o operație atât de simplă. Ce se întâmplă dacă trebuie să-l programăm ca funcție. Se pare că pentru fiecare tip de date va trebui să vă creați propria funcție. Adică înțelegi tu însuți că același cod va fi în mai multe copii, acest lucru nu ne este de nici un folos. De aceea, C++ a venit cu un astfel de mecanism - șabloane de funcție.

Creăm un șablon în care descriem toate tipurile de date. În acest fel, sursa nu va fi aglomerată cu linii de cod inutile. Mai jos ne vom uita la un exemplu de program cu un șablon de funcție. Deci, să ne amintim condiția: „programați o funcție care ar afișa elementele matricei”.

#include < count; ix++) cout << array << " "; cout << endl; } // конец шаблона функции printArray int main() { // размеры массивов const int iSize = 10, dSize = 7, fSize = 10, cSize = 5; // массивы разных типов данных int iArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double dArray = {1.2345, 2.234, 3.57, 4.67876, 5.346, 6.1545, 7.7682}; float fArray = {1.34, 2.37, 3.23, 4.8, 5.879, 6.345, 73.434, 8.82, 9.33, 10.4}; char cArray = {"MARS"}; cout << "\t\t Шаблон функции вывода массива на экран\n\n"; // вызов локальной версии функции printArray для типа int через шаблон cout << "\nМассив типа int:\n"; printArray(iArray, iSize); // вызов локальной версии функции printArray для типа double через шаблон cout << "\nМассив типа double:\n"; printArray(dArray, dSize); // вызов локальной версии функции printArray для типа float через шаблон cout << "\nМассив типа float:\n"; printArray(fArray, fSize); // вызов локальной версии функции printArray для типа char через шаблон cout << "\nМассив типа char:\n";printArray(cArray, cSize); return 0; }

// cod Cod::Blocuri

// Cod Dev-C++

#include #include folosind namespace std; // template template printArray template void printArray(const T * array, int count) (pentru (int ix = 0; ix< count; ix++) cout << array << " "; cout << endl; } // конец шаблона функции printArray int main() { // размеры массивов const int iSize = 10, dSize = 7, fSize = 10, cSize = 5; // массивы разных типов данных int iArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double dArray = {1.2345, 2.234, 3.57, 4.67876, 5.346, 6.1545, 7.7682}; float fArray = {1.34, 2.37, 3.23, 4.8, 5.879, 6.345, 73.434, 8.82, 9.33, 10.4}; char cArray = {"MARS"}; cout << "\t\t Шаблон функции вывода массива на экран\n\n"; // вызов локальной версии функции printArray для типа int через шаблон cout << "\nМассив типа int:\n"; printArray(iArray, iSize); // вызов локальной версии функции printArray для типа double через шаблон cout << "\nМассив типа double:\n"; printArray(dArray, dSize); // вызов локальной версии функции printArray для типа float через шаблон cout << "\nМассив типа float:\n"; printArray(fArray, fSize); // вызов локальной версии функции printArray для типа char через шаблон cout << "\nМассив типа char:\n";printArray(cArray, cSize); return 0; }

Vă rugăm să rețineți că codul a fost redus de 4 ori, deoarece în program este declarată o singură instanță a funcției - șablonul. În principal, am declarat mai multe matrice - patru, pentru tipurile de date: int, double, float, char. După care, în rândurile 26, 28, 30, 32, funcția printArray este apelată pentru diferite matrice. Rezultatul programului este prezentat mai jos.

Șablon pentru afișarea unui tablou pe ecran Matrice de tip int: 1 2 3 4 5 6 7 8 9 10 Matrice de tip dublu: 1.2345 2.234 3.57 4.67876 5.346 6.1545 7.7682 Matricea de tip float 6. 345 73.434 8.82 9.33 10.4 Matrice de tip char: M A R S

După cum puteți vedea, programul funcționează corect și pentru aceasta nu a trebuit decât să definim funcția printArray o singură dată în forma care ne este familiară. Vă rugăm să rețineți că înainte de declararea funcției în sine, pe linia 5, există următoarea intrare de șablon . Această intrare sugerează că funcția printArray este de fapt un șablon de funcție, deoarece primul parametru al printArray conține un tip de date const T*, exact la fel ca în linia 5.

Toate șabloanele de funcție încep cu cuvântul șablon urmat de paranteze unghiulare care listează parametrii. Fiecare parametru trebuie să fie precedat de clasa sau tipul de cuvânt rezervat.

Șablon

Șablon

Șablon

Cuvântul cheie typename îi spune compilatorului că șablonul va folosi un tip de date încorporat, cum ar fi: int , double , float , char , etc. Și cuvântul cheie class îi spune compilatorului că șablonul de funcție va folosi tipuri de date personalizate ca parametru , adică cursuri.

Șablonul nostru de funcție a folosit tipuri de date încorporate, așa că pe linia 5 am scris șablon . În loc de T, puteți înlocui orice alt nume la care vă puteți gândi. Să aruncăm o privire mai atentă la fragmentul de cod din programul de sus, îl voi posta separat.

// template template printArray template void printArray(const T * array, int count) (pentru (int ix = 0; ix< count; ix++) cout << array << " "; cout << endl; } // конец шаблона функции printArray

Linia 2 definește un șablon cu un parametru, T, iar acest parametru va avea unul dintre tipurile de date încorporate deoarece este specificat cuvântul cheie typename.

Mai jos, în rândurile 3 - 8, este declarată o funcție care îndeplinește toate criteriile pentru declararea unei funcții obișnuite, există un antet, există un corp de funcție, antetul conține numele și parametrii funcției, totul este ca de obicei. Dar ceea ce transformă această funcție într-un șablon de funcție este un parametru cu tipul de date T , care este singura conexiune la șablonul declarat mai devreme. Daca am scris

Void printArray(const int * array, int count) (pentru (int ix = 0; ix< count; ix++) cout << array << " "; cout << endl; }

atunci ar fi o funcție simplă pentru o matrice de tip int .

Deci, de fapt, T nici măcar nu este un tip de date, este un loc rezervat pentru orice tip de date încorporat. Adică, atunci când această funcție este apelată, compilatorul analizează parametrul funcției șablon și creează o instanță pentru tipul de date adecvat: int, char și așa mai departe.

Prin urmare, ar trebui să înțelegeți că, chiar dacă cantitatea de cod este mai mică, acest lucru nu înseamnă că programul va consuma mai puțină memorie. Compilatorul însuși creează copii locale ale funcției șablon și, în consecință, cantitatea de memorie consumată este ca și cum ați fi scris singur toate instanțele funcției, așa cum este cazul supraîncărcării.
Sper că v-am transmis ideea principală despre șabloanele de funcții. Pentru a consolida materialul, să ne uităm la un alt exemplu de program folosind un șablon de funcție.

#include „stdafx.h” #include #include < size; ix++) if (max < array) max = array; return max; } int main() { // тестируем шаблон функции searchMax для массива типа char char array = "aodsiafgerkeio"; int len = strlen(array); cout << "Максимальный элемент массива типа char: " << searchMax(array, len) << endl; // тестируем шаблон функции searchMax для массива типа int int iArray = {3,5,7,2,9}; cout << "Максимальный элемент массива типа int: " << searchMax(iArray, 5) << endl; return 0; }

// cod Cod::Blocuri

// Cod Dev-C++

#include #include folosind namespace std; // șablon de funcție pentru găsirea valorii maxime în șablonul matrice T searchMax(const T* array, int size) ( T max = matrice; // valoarea maximă în matrice pentru (int ix = 0; ix< size; ix++) if (max < array) max = array; return max; } int main() { // тестируем шаблон функции searchMax для массива типа char char array = "aodsiafgerkeio"; int len = strlen(array); cout << "Максимальный элемент массива типа char: " << searchMax(array, len) << endl; // тестируем шаблон функции searchMax для массива типа int int iArray = {3,5,7,2,9}; cout << "Максимальный элемент массива типа int: " << searchMax(iArray, 5) << endl; return 0; }

Iată un alt exemplu de utilizare a șabloanelor de funcție. Șablonul funcției este declarat pe rândurile 5-13. Funcția trebuie să returneze valoarea maximă a matricei, deci valoarea returnată este de tip T , deoarece tipul de date al matricei nu este cunoscut în prealabil. Apropo, în interiorul funcției este declarată o variabilă max de tip T; valoarea maximă a matricei va fi stocată în ea. După cum puteți vedea, tipul de date T este folosit nu numai pentru a specifica parametrii funcției, ci și pentru a indica tipul de returnare și poate fi folosit în mod liber pentru a declara orice variabile într-un șablon de funcție.

Element maxim al unui tablou de tip char: s Element maxim al unui tablou de tip int: 9

Șabloanele de funcție pot fi, de asemenea, supraîncărcate cu alte șabloane de funcție prin modificarea numărului de parametri trecuți funcției. O altă caracteristică a supraîncărcării este că funcțiile șablon pot fi supraîncărcate cu funcții obișnuite care nu sunt șablon. Adică este specificat același nume de funcție, cu aceiași parametri, dar pentru un anumit tip de date și totul va funcționa corect.

Eram pe cale să scriu un text despre tot felul de structuri de date interesante și apoi s-a dovedit că încă nu examinasem câteva caracteristici foarte importante ale C++. Șabloanele sunt unul dintre ele.

Șabloanele sunt un instrument foarte puternic. Funcțiile și clasele șablonului pot simplifica foarte mult viața unui programator și pot economisi o cantitate imensă de timp, efort și nervi. Dacă credeți că șabloanele nu sunt un subiect foarte semnificativ de studiat, să știți că vă înșelați.

Funcții de șablon

Un exemplu simplu de funcție șablon:

cod C++ Type square (Tip a) ( Tip b; b = a*a; return b; ) int x = 5; int i; i = pătrat(5); float y = 0,5; plutitor f; f = pătrat(y);

Dacă am creat funcții în mod vechi, atunci ar trebui să scriem două funcții diferite: pentru tipul int și pentru tipul float. Și dacă aveți nevoie de aceeași funcție folosind alte tipuri, ar trebui să o scrieți din nou. Folosind șabloane, vă puteți limita la o singură instanță a unei funcții, lăsând toată munca murdară în seama compilatorului.

În loc să folosească un anumit tip, funcția folosește un tip parametric (sau, cu alte cuvinte, un argument șablon). Aici am numit tipul parametric identificatorul Tip. Acest identificator apare de trei ori într-o funcție: valoarea returnată, argumentul funcției și definiția variabilei s. Adică, Type este folosit ca orice tip obișnuit.

Dar pentru ca codul să funcționeze, trebuie să adăugați următoarea linie înainte de funcție (am arătat mai multe opțiuni de sintaxă, toate funcționează):

cod C++șablon Tastați șablonul pătrat (Tip a). < class Type >Tastați șablonul pătrat (Tip a).< class Type >Tip pătrat (Tip a)

Deci, funcția trebuie să fie precedată de șablonul de cuvinte cheie, iar între paranteze unghiulare trebuie să specificați numele tipului parametric cu clasa de cuvinte cheie. În loc de cuvântul cheie class, puteți folosi type - în general, nu există nicio diferență.

Identificatorul de tip parametric poate fi, de asemenea, orice. Vom folosi adesea acestea: TypeA, TypeB, Datatype, T.

Notă importantă: Funcțiile șablon trebuie să aibă un argument, astfel încât compilatorul să poată determina ce tip să folosească.

Puteți folosi mai multe tipuri parametrice în șabloane și, desigur, puteți amesteca tipurile parametrice cu cele standard (trebuie doar să vă ocupați de turnarea corectă a tipului). Voi da un exemplu care utilizează două tipuri parametrice TypeA, TypeB și tipul de bază int:

cod C++șablon Exemplu_funcție de tip B (TypeA a, TypeB b) ( int x = 5; b = a + x; return b; )

Dar funcțiile șablon nu sunt cel mai interesant lucru la care ne vom uita astăzi.

Clasele șablon

În general, clasele de șablon sunt create în același mod ca și funcțiile șablon - șablonul de cuvinte cheie este scris înainte de numele clasei. Să ne uităm la clasele șabloane folosind o stivă ca exemplu:

cod C++șablon clasă stivă ( private: int top; Type s; public: stack (): top(0) () void push(Type var) ( top++; s = var; ) Type pop(); ); șablon Tip stack::pop() (Tip var = s; top--; return var; ) Aici definim

a împărțit un teanc de zece elemente. Aceste elemente pot fi de orice tip, mai multe despre cele de mai jos.

Singurul lucru asupra căruia vreau să vă atrag atenția este definirea funcțiilor push și pop. Funcția push este definită în interiorul clasei, iar funcția pop este definită în afara acesteia. Pentru toate funcțiile declarate în afara clasei, trebuie specificat cuvântul cheie șablon. Expresia dinaintea numelui funcției este aceeași cu cea dinaintea numelui clasei.

Acum să vedem cum să lucrăm cu clasele șablon:

cod C++ grămadă s1; grămadă s2; s1.push(3); s1.push(2); s1.pop(); s2.push(0,5);

Când creați un obiect, după numele clasei trebuie să puneți paranteze unghiulare în care să indicați tipul dorit. După aceasta, obiectele sunt folosite așa cum ne-am obișnuit.

Clasele șabloane au o caracteristică uimitoare - pe lângă tipurile standard, pot funcționa și cu cele personalizate. Să ne uităm la un mic exemplu. Pentru a face acest lucru, să definim o clasă simplă de războinici:

cod C++ războinic de clasă ( public: int sănătate; războinic () : sănătate(0) () ); grămadă s; războinic w1; războinic w2; războinic w3; s.împinge(w1); s.împinge(w3); s.pop(); s.împinge(w2);

Uite, acum poți pune variabile de tip războinic pe stive!!! Poate că nu mă credeți, dar asta este foarte tare! Puteți vedea cât de tare este acest lucru când creăm grafice și arbori pe baza listelor.

Asta e totul conform șabloanelor deocamdată. Mai târziu ne vom uita la cazuri mai complexe de utilizare a claselor șablon.