Funcții în linie (încorporate). Supraîncărcarea funcției

Supraîncărcarea funcției

Supraîncărcarea operațiunilor (operatori, funcții, proceduri)- în programare - una dintre modalitățile de implementare a polimorfismului, care constă în posibilitatea existenței simultane într-un singur domeniu a mai multor diverse opțiuni operațiuni (instrucțiune, funcție sau procedură) care au același nume, dar diferă prin tipurile de parametri cărora li se aplică.

Terminologie

Termenul „supraîncărcare” este o hârtie de calc a „supraîncărcării” engleză, care a apărut în traducerile ruse ale cărților despre limbaje de programare în prima jumătate a anilor 1990. Poate că acesta nu este cel mai mult cea mai bună opțiune traducere, deoarece cuvântul „supraîncărcare” în limba rusă are un înțeles propriu stabilit, radical diferit de cel recent propus, cu toate acestea, a prins rădăcini și este destul de răspândit. În publicațiile din epoca sovietică, mecanisme similare au fost numite în rusă „redefinirea” sau „redefinirea” operațiunilor, dar această opțiune nu este discutabilă: apar discrepanțe și confuzii în traducerile englezei „override”, „supraîncărcare” și „ redefiniți”.

Motivele apariției

Majoritatea limbajelor de programare timpurii aveau o restricție conform căreia nu mai mult de o operație cu același nume putea fi disponibilă într-un program în același timp. În consecință, toate funcțiile și procedurile vizibile la un punct dat în program trebuie să aibă nume diferite. Numele și denumirile funcțiilor, procedurilor și operatorilor care fac parte din limbajul de programare nu pot fi folosite de programator pentru a-și denumi propriile funcții, proceduri și operatori. În unele cazuri, un programator își poate crea propriul obiect program cu numele altuia deja existent, dar apoi obiectul nou creat se „suprapune” pe cel anterior și devine imposibil să folosești ambele opțiuni în același timp.

Această situație este incomodă în unele cazuri destul de frecvente.

  • Uneori este nevoie de a descrie și de a aplica operațiuni la tipurile de date create de programator care sunt echivalente ca semnificație cu cele deja disponibile în limbaj. Un exemplu clasic este o bibliotecă pentru lucrul cu numere complexe. Ele, ca și tipurile numerice obișnuite, acceptă operații aritmetice și ar fi firesc să creați pentru de acest tip operațiile „plus”, „minus”, „înmulțire”, „împărțire”, notându-le cu aceleași semne de operație ca și pentru alte tipuri numerice. Interdicția utilizării elementelor definite în limbaj obligă la crearea multor funcții cu nume precum ComplexPlusComplex, IntegerPlusComplex, ComplexMinusFloat și așa mai departe.
  • Când operanzilor se aplică operanzii de aceeași semnificație tipuri variate, ele trebuie denumite diferit. Incapacitatea de a aplica tipuri diferite funcțiile cu același nume duce la necesitatea de a inventa nume diferite pentru același lucru, ceea ce creează confuzie și poate duce la erori. De exemplu, în limbajul clasic C, există două versiuni ale funcției standard de bibliotecă pentru găsirea modulului unui număr: abs() și fabs() - prima este destinată unui argument întreg, a doua pentru unul real. Această situație, combinată cu verificarea slabă de tip C, poate duce la o eroare greu de găsit: dacă un programator scrie abs(x) în calcul, unde x este o variabilă reală, atunci unii compilatori vor genera cod fără avertisment care va convertiți x într-un număr întreg eliminând părțile fracționale și calculați modulul din întregul rezultat!

În parte, problema este rezolvată prin intermediul programării obiectelor - atunci când noi tipuri de date sunt declarate ca clase, operațiunile asupra acestora pot fi formalizate ca metode de clasă, inclusiv metode de clasă cu același nume (deoarece metodele din clase diferite nu trebuie să aibă nume diferite), dar, în primul rând, un astfel de mod de proiectare a operațiunilor pe valori de diferite tipuri este incomod și, în al doilea rând, nu rezolvă problema creării de noi operatori.

Supraîncărcarea operatorului în sine este doar „zahăr sintactic”, deși chiar și ca atare poate fi utilă deoarece permite dezvoltatorului să programeze într-un mod mai natural și face ca tipurile personalizate să se comporte mai mult ca cele încorporate. Dacă abordăm problema dintr-o poziție mai generală, atunci putem vedea că instrumentele care vă permit să extindeți limbajul îl completează cu noi operații și construcții sintactice (și supraîncărcarea operațiilor este unul dintre astfel de instrumente, alături de obiecte, macro-uri). , funcționale, închideri) îl transformă deja în metalimbaj - un mijloc de descriere a limbilor orientate către sarcini specifice. Cu ajutorul acestuia, este posibil să construiți o extensie de limbaj pentru fiecare sarcină specifică care este cea mai potrivită pentru aceasta, ceea ce va permite descrierea soluției sale în forma cea mai naturală, mai ușor de înțeles și mai simplă. De exemplu, într-o aplicație de supraîncărcare a operațiilor: crearea unei biblioteci de tipuri matematice complexe (vectori, matrice) și descrierea operațiilor cu acestea într-o formă naturală, „matematică”, creează un „limbaj pentru operații vectoriale”, în care complexitatea calculele este ascunsă și este posibil să se descrie soluția problemelor în termeni de operații vectoriale și matrice, concentrându-se pe esența problemei, nu pe tehnică. Din aceste motive, astfel de mijloace au fost odată incluse în limba Algol-68.

Mecanism de suprasarcină

Implementarea

Supraîncărcarea operatorului implică introducerea a două caracteristici interdependente în limbaj: capacitatea de a declara mai multe proceduri sau funcții cu același nume în același domeniu și capacitatea de a descrie propriile implementări ale operațiunilor (adică semnele operațiunilor, de obicei scris cu notație infixă, între operanzi). Practic, implementarea lor este destul de simplă:

  • Pentru a permite existența mai multor operații cu același nume, este suficient să se introducă în limbaj o regulă, conform căreia o operație (procedură, funcție sau operator) este recunoscută de compilator nu numai după nume (notație), ci de asemenea prin tipurile parametrilor lor. Deci abs(i), unde i este declarat ca un întreg, și abs(x), unde x este declarat ca real, sunt două operații diferite. În principiu, nu există dificultăți în a oferi doar o astfel de interpretare.
  • Pentru a permite operațiunile de definire și redefinire, este necesar să se introducă în limbaj construcții sintactice adecvate. Pot exista destul de multe opțiuni, dar de fapt nu diferă unele de altele, este suficient să ne amintim că introducerea formularului „<операнд1> <знакОперации> <операнд2>» este fundamental similar cu apelarea funcției «<знакОперации>(<операнд1>,<операнд2>)". Este suficient să permiteți programatorului să descrie comportamentul operatorilor sub formă de funcții - iar problema descrierii este rezolvată.

Opțiuni și probleme

Supraîncărcarea procedurilor și funcțiilor la nivel idee comună, de regulă, nu este greu nici de implementat, nici de înțeles. Cu toate acestea, chiar și în el există câteva „capcane” care trebuie luate în considerare. Permiterea supraîncărcării operatorului creează mult mai multe probleme atât pentru implementatorul limbajului, cât și pentru programatorul care lucrează în limba respectivă.

Problema de identificare

Prima întrebare cu care se confruntă un dezvoltator al unui traducător de limbi care permite supraîncărcarea procedurilor și funcțiilor este cum să aleagă dintre procedurile cu același nume pe cea care ar trebui aplicată într-un anumit caz concret? Totul este în regulă dacă există o variantă a procedurii, ale căror tipuri de parametri formali se potrivesc exact cu tipurile de parametrii efectivi utilizați în acest apel. Cu toate acestea, în aproape toate limbile, există un anumit grad de libertate în utilizarea tipurilor, presupunând că compilatorul realizează automat conversii sigure de tip în anumite situații. De exemplu, în operațiile aritmetice pe argumente reale și întregi, întregul este de obicei convertit automat în tip real, iar rezultatul este real. Să presupunem că există două variante ale funcției de adăugare:

int add(int a1, int a2); float add(float a1, float a2);

Cum ar trebui să gestioneze compilatorul expresia y = add(x, i) unde x este un float și i este un int? Evident, nu există o potrivire exactă. Există două opțiuni: fie y=add_int((int)x,i) , fie ca y=add_flt(x, (float)i) (aici numele add_int și add_float denotă prima și, respectiv, a doua versiune a funcției) .

Se pune întrebarea: compilatorul ar trebui să permită această utilizare a funcțiilor supraîncărcate și, dacă da, pe ce bază va alege varianta particulară utilizată? În special, în exemplul de mai sus, traducătorul ar trebui să ia în considerare tipul variabilei y atunci când alege? Trebuie remarcat faptul că situația de mai sus este cea mai simplă, sunt posibile cazuri mult mai complicate, care sunt agravate de faptul că nu numai tipurile încorporate pot fi convertite conform regulilor limbajului, ci și clasele declarate de programator. , dacă au relații de rudenie, pot fi aruncați unul la altul. Există două soluții la această problemă:

  • Interziceți deloc identificarea incorectă. Solicitați ca pentru fiecare pereche particulară de tipuri să existe o variantă exactă potrivită a procedurii sau operațiunii supraîncărcate. Dacă nu există o astfel de opțiune, compilatorul ar trebui să arunce o eroare. În acest caz, programatorul trebuie să aplice o conversie explicită pentru a transforma parametrii actuali la setul de tipuri dorit. Această abordare este incomodă în limbaje precum C++, care permit o libertate destul de mare în tratarea tipurilor, deoarece duce la o diferență semnificativă în comportamentul operatorilor încorporați și supraîncărcați (operațiile aritmetice pot fi aplicate numerelor obișnuite fără a sta pe gânduri, ci la alte tipuri - numai cu conversie explicită) sau la apariția unui număr mare de opțiuni pentru operațiuni.
  • Stabiliți anumite reguli pentru alegerea „cea mai apropiată potrivire”. De obicei, în această variantă, compilatorul le alege pe acelea dintre variantele ale căror apeluri pot fi obținute din sursă doar prin conversii de tip sigur (informații fără pierderi), iar dacă sunt mai multe, poate alege în funcție de care variantă necesită mai puține. astfel de conversii. Dacă rezultatul lasă mai multe posibilități, compilatorul aruncă o eroare și solicită programatorului să specifice în mod explicit varianta.

Considerații specifice privind supraîncărcarea operației

Spre deosebire de proceduri și funcții, operațiunile infixe ale limbajelor de programare au două proprietăți suplimentare care le afectează în mod semnificativ funcționalitatea: prioritatea și asociativitatea, a căror prezență se datorează posibilității de înregistrare „în lanț” a operatorilor (cum să înțelegem a + b * c: ca (a + b )*c sau ca a+(b*c) ? Expresia a-b+c este (a-b)+c sau a-(b+c) ?).

Operațiile încorporate în limbaj au întotdeauna precedență tradițională și asociativitate predefinite. Se pune întrebarea: ce priorități și asociativitate vor avea versiunile redefinite ale acestor operații sau, mai mult, noile operații create de programator? Există și alte subtilități care ar putea necesita clarificări. De exemplu, în C există două forme de operatori de creștere și decrementare ++ și -- - prefix și postfix, care se comportă diferit. Cum ar trebui să se comporte versiunile supraîncărcate ale unor astfel de operatori?

Diferite limbi abordează aceste probleme în moduri diferite. Astfel, în C++, precedența și asociativitatea versiunilor supraîncărcate ale operatorilor sunt păstrate aceleași cu cele definite în limbaj; este posibil să supraîncărcați separat formele de prefix și postfix ale operatorilor de creștere și decrementare folosind semnături speciale:

Deci int este folosit pentru a face diferența în semnături

Anunțul de noi operațiuni

Situația cu anunțul de noi operațiuni este și mai complicată. Includerea posibilității unei astfel de declarații în limbă nu este dificilă, dar implementarea acesteia este plină de dificultăți semnificative. Declararea unei noi operațiuni înseamnă, de fapt, crearea unei noi cuvânt cheie limbaj de programare, complicat de faptul că operațiunile din text, de regulă, pot urma fără separatori cu alte jetoane. Când apar, apar dificultăți suplimentare în organizarea analizatorului lexical. De exemplu, dacă limbajul are deja operațiile „+” și unara „-” (schimbarea semnului), atunci expresia a+-b poate fi interpretată cu precizie ca a + (-b), dar dacă o nouă operație +- este declarată în program, apare imediat ambiguitate, deoarece aceeași expresie poate fi deja analizată ca a (+-) b . Dezvoltatorul și implementatorul limbajului trebuie să se ocupe de astfel de probleme într-un fel. Opțiunile, din nou, pot fi diferite: necesită ca toate operațiunile noi să fie cu un singur caracter, postulează că, în cazul oricăror discrepanțe, se alege cea mai „lungă” versiune a operației (adică până la următorul set de caractere citit de către translator se potrivește cu orice operațiune, continuă să fie citit), încearcă să detecteze coliziuni în timpul traducerii și să genereze erori în cazuri controversate... Într-un fel sau altul, limbile care permit declararea de noi operațiuni rezolvă aceste probleme.

Nu trebuie uitat că pentru operațiuni noi se pune și problema determinării asociativității și priorității. Nu mai există o soluție gata făcută sub forma unei operațiuni de limbaj standard și, de obicei, trebuie doar să setați acești parametri cu regulile limbii. De exemplu, faceți toate operațiunile noi asociate stânga și acordați-le aceeași, fixă, prioritate sau introduceți în limbaj mijloacele de specificare a ambelor.

Supraîncărcare și variabile polimorfe

Când operatorii, funcțiile și procedurile supraîncărcate sunt utilizate în limbaje puternic tipizate, în care fiecare variabilă are un tip pre-declarat, este la latitudinea compilatorului să decidă ce versiune a operatorului supraîncărcat să folosească în fiecare caz particular, indiferent cât de complexă. . Aceasta înseamnă că pentru limbajele compilate, utilizarea supraîncărcării operatorului nu duce la degradarea performanței - în orice caz, există o operație sau un apel de funcție bine definit în codul obiect al programului. Situația este diferită atunci când este posibil să se utilizeze variabile polimorfe în limbaj, adică variabile care pot conține valori de diferite tipuri în momente diferite.

Deoarece tipul valorii la care se va aplica operația supraîncărcată nu este cunoscut în momentul traducerii codului, compilatorul nu poate alege opțiunea dorită anticipat. În acest caz, este forțat să încorporați un fragment în codul obiect care, imediat înainte de efectuarea acestei operațiuni, va determina tipurile de valori din argumente și va selecta dinamic o variantă corespunzătoare acestui set de tipuri. Mai mult, o astfel de definiție trebuie făcută la fiecare execuție a operației, deoarece chiar și același cod, fiind numit a doua oară, poate fi executat diferit.

Astfel, utilizarea supraîncărcării operatorului în combinație cu variabile polimorfe face inevitabil să se determine în mod dinamic ce cod să apeleze.

Critică

Utilizarea supraîncărcării nu este considerată un avantaj de către toți experții. Dacă supraîncărcarea funcțiilor și procedurilor nu este, în general, inacceptabilă (parțial pentru că nu duce la unele probleme tipice de „operator”, parțial pentru că este mai puțin tentant să o folosiți greșit), atunci supraîncărcarea operatorului este, în principiu, și în anumite implementări ale limbajului, este supus unor critici destul de severe din partea multor teoreticieni și practicieni ai programării.

Criticii subliniază că problemele de identificare, precedență și asociativitate evidențiate mai sus fac adesea ca tratarea cu operatori supraîncărcați fie inutil de dificilă, fie nefirească:

  • Identificare. Dacă limbajul are reguli stricte de identificare, atunci programatorul este forțat să-și amintească pentru ce combinații de tipuri există operații supraîncărcate și să le transmită manual operanzi. Dacă limbajul permite identificarea „aproximativă”, nu se poate fi niciodată sigur că într-o situație destul de complicată se va executa exact varianta operației pe care programatorul o avea în vedere.
  • Prioritate și asociativitate. Dacă sunt definite rigid, acest lucru poate fi incomod și nu este relevant pentru domeniul subiectului (de exemplu, pentru operațiuni cu seturi, prioritățile diferă de cele aritmetice). Dacă pot fi setate de programator, aceasta devine o sursă suplimentară de erori (fie și numai pentru că diferite variante ale unei singure operații se dovedesc a avea priorități diferite, sau chiar asociativitate).

Cât de mult poate depăși comoditatea utilizării propriilor operațiuni pe inconvenientul deteriorării controlabilității programului este o întrebare care nu are un răspuns clar.

Din punctul de vedere al implementării limbii, aceleași probleme duc la complexitatea traducătorilor și la scăderea eficienței și fiabilității acestora. Și utilizarea supraîncărcării împreună cu variabilele polimorfe este, de asemenea, evident mai lentă decât apelarea unei operații hardcoded în timpul compilării și oferă mai puține oportunități de optimizare a codului obiect. Caracteristicile specifice ale implementării supraîncărcării în diferite limbi sunt supuse unor critici separate. Deci, în C++, obiectul criticii poate fi lipsa unui acord privind reprezentarea internă a denumirilor de funcții supraîncărcate, ceea ce dă naștere la incompatibilități la nivelul bibliotecilor compilate de diferiți compilatori C++.

Unii critici vorbesc împotriva operațiunilor de supraîncărcare, pe baza principii generale teoria dezvoltării softwareși practică industrială reală.

  • Susținătorii abordării „puritane” a construirii limbajului, cum ar fi Wirth sau Hoare, se opun supraîncărcării operatorului pur și simplu pentru că se poate renunța cu ușurință. În opinia lor, astfel de instrumente nu fac decât să complice limba și traducătorul, fără a furniza corespunzătoare acestei complicații caracteristici suplimentare. În opinia lor, însăși ideea de a crea o extensie a limbii orientată spre sarcini pare doar atractivă. În realitate, utilizarea instrumentelor de extensie a limbajului face ca programul să fie înțeles doar pentru autorul său - cel care a dezvoltat această extensie. Programul devine mult mai dificil de înțeles și analizat pentru alți programatori, ceea ce face mai dificilă întreținerea, modificarea și dezvoltarea echipei.
  • Se observă că însăși posibilitatea utilizării supraîncărcării joacă adesea un rol provocator: programatorii încep să o folosească oriunde este posibil, ca urmare, un instrument conceput pentru a simplifica și eficientiza programul devine cauza complicației și confuziei sale.
  • Este posibil ca operatorii supraîncărcați să nu facă exact ceea ce se așteaptă de la ei, în funcție de felul lor. De exemplu, a + b de obicei (dar nu întotdeauna) înseamnă același lucru ca b + a , dar „unu” + „două” este diferit de „două” + „unu” în limbile în care operatorul + este supraîncărcat pentru concatenare de șiruri.
  • Supraîncărcarea operatorului face fragmentele de program mai sensibile la context. Fără a cunoaște tipurile de operanzi implicați într-o expresie, este imposibil de înțeles ce face expresia dacă folosește operatori supraîncărcați. De exemplu, într-un program C++, instrucțiunea<< может означать и побитовый сдвиг, и вывод в поток. Выражение a << 1 возвращает результат побитового сдвига значения a на один бит влево, если a - целая переменная, но если a является выходным потоком , то же выражение выведет в этот поток строку «1» .

Clasificare

Următoarea este o clasificare a unor limbaje de programare în funcție de faptul dacă permit supraîncărcarea operatorului și dacă operatorii sunt limitați la un set predefinit:

Operațiuni Fără supraîncărcare Există o supraîncărcare
Set limitat de operațiuni
  • Obiectiv-C
  • Piton
Este posibil să se definească noi operațiuni
  • PostgreSQL
  • Vezi si

    Fundația Wikimedia. 2010 .

    Vedeți ce este „Supraîncărcarea funcției” în alte dicționare:

      - (operatori, funcții, proceduri) în programarea uneia dintre modalitățile de implementare a polimorfismului, care constă în posibilitatea existenței simultane într-un singur domeniu a mai multor variante diferite ale unei operații (operator, funcție sau ... ... Wikipedia

Supraîncărcarea funcțiilor este definiția mai multor funcții (două sau mai multe) cu același nume, dar cu parametri diferiți. Seturile de parametri ai funcțiilor supraîncărcate pot diferi în ordine, număr și tip. Astfel, este necesară supraîncărcarea funcțiilor pentru a evita duplicarea denumirilor funcțiilor care efectuează acțiuni similare, dar cu o logică de program diferită. De exemplu, luați în considerare funcția areaRectangle(), care calculează aria unui dreptunghi.

Float areaRectangle(float, float) //funcție care calculează aria unui dreptunghi cu doi parametri a(cm) și b(cm) ( returnează a * b; // înmulțim lungimile laturilor dreptunghiului și returnăm produsul rezultat)

Deci, aceasta este o funcție cu doi parametri de tip float, iar argumentele transmise funcției trebuie să fie în centimetri, valoarea returnată a tipului float este tot în centimetri.

Să presupunem că datele noastre inițiale (laturile dreptunghiulare) sunt date în metri și centimetri, de exemplu: a = 2m 35 cm; b = 1m 86 cm.În acest caz, ar fi convenabil să folosiți o funcție cu patru parametri. Adică, fiecare lungime a laturilor dreptunghiului este transmisă funcției în doi parametri: metri și centimetri.

Float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // funcție care calculează aria unui dreptunghi cu 4 parametri a(m) a(cm); b(m) b(cm) ( return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); )

În corpul funcției, valorile care au fost transmise în metri (a_m și b_m) sunt convertite în centimetri și adăugate la valorile a_sm b_sm , după care înmulțim sumele și obținem aria dreptunghiului în cm. Desigur, a fost posibil să convertiți datele originale în centimetri și să folosiți prima funcție, dar acum nu este vorba despre asta.

Acum, cel mai important este că avem două funcții, cu semnături diferite, dar aceleași nume (funcții supraîncărcate). O semnătură este o combinație a unui nume de funcție cu parametrii săi. Cum se apelează aceste funcții? Și apelarea funcțiilor supraîncărcate nu este diferită de apelarea funcțiilor obișnuite, de exemplu:

ZonaRectunghi(32, 43); // se va apela o funcție care calculează aria unui dreptunghi cu doi parametri a(cm) și b(cm) areaRectangle(4, 43, 2, 12); // se va apela o funcție care calculează aria unui dreptunghi cu 4 parametri a(m) a(cm); b(m) b(cm)

După cum puteți vedea, compilatorul va selecta independent funcția dorită, analizând doar semnăturile funcțiilor supraîncărcate. Ocolind supraîncărcarea funcției, s-ar putea declara pur și simplu o funcție cu un alt nume și și-ar face bine treaba. Dar imaginați-vă ce se va întâmpla dacă aveți nevoie de mai mult de două astfel de funcții, de exemplu 10. Și pentru fiecare trebuie să veniți cu un nume semnificativ, iar cel mai greu lucru de reținut sunt acestea. Tocmai de aceea este mai ușor și mai bine să supraîncărcați funcțiile, cu excepția cazului în care, desigur, este nevoie de acest lucru. Codul sursă al programului este prezentat mai jos.

#include „stdafx.h” #include << "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

// cod Cod::Blocuri

// Cod Dev-C++

#include folosind namespace std; // prototipuri de funcții supraîncărcate float areaRectangle(float a, float b); float areaRectangle(float a_m, float a_sm, float b_m, float b_sm); int main() ( cout<< "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1 cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2 return 0; } // перегруженная функция 1 float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см) { return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение } // перегруженная функция 2 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм) { return (a_m * 100 + a_sm) * (b_m * 100 + b_sm); }

Rezultatul programului este prezentat în Figura 1.



Cum se obține supraîncărcarea funcției în C? (10)

Există o modalitate de a obține supraîncărcarea funcției în C? Mă uit la funcții simple care pot fi supraîncărcate cum ar fi

foo (int a) foo (char b) foo (float c , int d)

Cred că nu există o cale directă; Caut soluții, dacă există.

Sper că codul de mai jos vă va ajuta să înțelegeți supraîncărcarea funcției

#include #include int fun(int a, ...); int main(int argc, char *argv)( fun(1,10); fun(2,"cquestionbank"); return 0; ) int fun(int a, ...)( va_list vl; va_start(vl,a) ); if(a==1) printf("%d",va_arg(vl,int)); else printf("\n%s",va_arg(vl,char *)); )

Adică, vrei să spui - nu, nu poți.

Puteți declara funcția va_arg ca

void my_func(char* format, ...);

Dar va trebui să transmiteți câteva informații despre numărul de variabile și tipurile lor în primul argument - cum ar fi printf() .

Da, ca.

Aici dai un exemplu:

void printA(int a)( printf("Buna ziua de la printA: %d\n",a); ) void printB(const char *buff)( printf("Bună ziua de la printB: %s\n",buff) ; ) #define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0 #define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N #define _Num_ARGS_(... ) __VA_ARG_N(__VA_ARGS__) #define NUM_ARGS(...) (_Num_ARGS_(_0, ## __VA_ARGS__, Max_ITEMS())) - 1) #define CHECK_ARGS_MAX_LIMIT(t) if(NUM_ARGS(args(args))_MIT) #define if(NUM_ARGS(args) #define print(x , args ...) \ CHECK_ARGS_MIN_LIMIT(1) printf("eroare");fflush(stdout); \ CHECK_ARGS_MAX_LIMIT(4) printf("eroare");fflush(stdout) ; \ (( \ if (__builtin_types_compatible_p (tip de (x), int)) \ printA(x, ##args); \ else \ printB (x,##args); \ )) int main(int argc, char* * argv) ( int a=0; print(a); print("bună ziua"); return (EXIT_SUCCESS); )

Va scoate 0 și salut de la printA și printB.

Dacă compilatorul tău este gcc și nu te deranjează să faci actualizări manuale de fiecare dată când adaugi o nouă supraîncărcare, poți să faci o masă macro și să obții rezultatul dorit din punctul de vedere al apelanților, nu atât de frumos de scris... dar este posibil

uită-te la __builtin_types_compatible_p apoi folosește-l pentru a defini o macrocomandă care face ceva de genul

#define foo(a) \ ((__builtin_types_compatible_p(int, a)?foo(a):(__builtin_types_compatible_p(float, a)?foo(a):)

dar da, urât, doar că nu

EDITAȚI | ×: C1X va primi suport pentru expresii de tip, care arată astfel:

#define cbrt(X) _Generic((X), long double: cbrtl, \ default: cbrt, \ float: cbrtf)(X)

După cum s-a menționat deja, supraîncărcarea în sensul la care vrei să spui nu este acceptată de C. Expresia obișnuită pentru a rezolva problema este ca funcția să ia o uniune etichetată. Acest lucru este implementat folosind parametrul struct, unde structura în sine constă dintr-un tip de indicator de tip, cum ar fi o enumerare și o uniune de diferite tipuri de valori. Exemplu:

#include typedef enum ( T_INT, T_FLOAT, T_CHAR, ) tipul_meu; typedef struct ( tipul meu_tip; unire ( int a; float b; char c; ) my_union; ) my_struct; void set_overload (my_struct *whatever) ( switch (whatever->type) (case T_INT: whatever->my_union.a = 1; break; case T_FLOAT: whatever->my_union.b = 2.0; break; case T_CHAR: whatever-> my_union.c = "3"; ) ) void printf_overload (my_struct *whatever) ( switch (whatever->type) (case T_INT: printf("%d\n", whatever->my_union.a); break; case T_FLOAT : printf("%f\n", orice->my_union.b); break; case T_CHAR: printf("%c\n", orice->my_union.c); break; ) ) int main (int argc, char* argv) ( my_struct s; s.type=T_INT; set_overload(&s); printf_overload(&s); s.type=T_FLOAT; set_overload(&s); printf_overload(&s); s.type=T_CHAR; set_overload(&s) ; printf_overload(&s); )

Nu poți să folosești C++ și să nu folosești toate celelalte caracteristici C++ în afară de aceasta?

Dacă până acum nu a existat C strict strict, aș recomanda în schimb funcții variadice.

Următoarea abordare este similară cu a2800276, dar cu unele macrocomenzi C99:

// avem nevoie de `size_t` #include // tipuri de argument pentru a accepta enumerarea sum_arg_types ( SUM_LONG, SUM_ULONG, SUM_DOUBLE ); // o structură care să dețină un argument struct sum_arg ( enum sum_arg_types type; union ( long as_long; unsigned long as_ulong; double as_double; ) valoare; ); // determinați dimensiunea unei matrice #define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY))) // așa se va numi funcția noastră #define sum(...) _sum( count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__)) // creează o matrice de `struct sum_arg` #define sum_args(...) ((struct sum_arg )( __VA_ARGS__ ))) // creează inițializatori pentru argumentele sum_ #define (VALOARE) ( SUM_LONG, ( .as_long = (VALUE) ) ) #define sum_ulong(VALUE) ( SUM_ULONG, ( .as_ulong = (VALUE) ) ) #define sum_double(VALUE) ( SUM_DOUBLE, ( .as_double = (VALUE) ) ) // funcția noastră polimorfă long double _sum(size_t count, struct sum_arg * args) (long double value = 0; for(size_t i = 0; i< count; ++i) { switch(args[i].type) { case SUM_LONG: value += args[i].value.as_long; break; case SUM_ULONG: value += args[i].value.as_ulong; break; case SUM_DOUBLE: value += args[i].value.as_double; break; } } return value; } // let"s see if it works #include int main() (unsigned long foo = -1; long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10)); printf("%Le\n", valoare); return 0; )

Deocamdată, _Generic, deoarece întrebarea _Generic, standard C (fără extensii) este efectiv primit suport pentru funcții de supraîncărcare (mai degrabă decât operatori) datorită adăugării cuvântului _Generic _Generic în C11. (acceptat în GCC începând cu versiunea 4.9)

(Supraîncărcarea nu este cu adevărat „încorporată” în modul arătat în întrebare, dar este ușor să distrugi ceva care funcționează astfel.)

Generic este un operator în timp de compilare din aceeași familie ca sizeof și _Alignof . Este descris în secțiunea standard 6.5.1.1. Este nevoie de doi parametri principali: o expresie (care nu va fi evaluată în timpul execuției) și o listă de asocieri de tip/expresie, care este puțin ca un bloc de comutare. _Generic primește tipul generic al expresiei și apoi „commută” la acesta pentru a selecta expresia rezultatului final din listă pentru tipul acesteia:

Generic(1, float: 2.0, char *: "2", int: 2, implicit: get_two_object());

Expresia de mai sus evaluează la 2 - tipul expresiei de control este int , deci selectează expresia asociată cu int ca valoare. Nimic din toate acestea nu este lăsat în timpul execuției. (Clauza implicită este obligatorie: dacă nu o specificați și tipul nu se potrivește, va provoca o eroare de compilare.)

O tehnică care este utilă pentru supraîncărcarea funcției este aceea că poate fi inserată de preprocesorul C și poate selecta o expresie de rezultat pe baza tipului de argumente transmise macro-ului de control. Deci (exemplu din standardul C):

#define cbrt(X) _Generic((X), \ long double: cbrtl, \ default: cbrt, \ float: cbrtf \)(X)

Această macrocomandă implementează operația cbrt supraîncărcată prin transmiterea tipului de argument la macrocomandă, selectând funcția de implementare adecvată și apoi trecând macrocomandă originală acelei funcție.

Deci, pentru a implementa exemplul dvs. original, am putea face acest lucru:

Foo_int (int a) foo_char (char b) foo_float_int (float c , int d) #define foo(_1, ...) _Generic((_1), \ int: foo_int, \ char: foo_char, \ float: _Generic(( FIRST(__VA_ARGS__,)), \int: foo_float_int))(_1, __VA_ARGS__) #define FIRST(A, ...) A

În acest caz, am putea folosi implicit: binding pentru al treilea caz, dar asta nu demonstrează cum să extindem principiul la mai multe argumente. Rezultatul final este că puteți folosi foo(...) în codul dvs. fără să vă faceți griji (mult) cu privire la tipul argumentelor dvs.

Pentru situații mai complexe, cum ar fi funcții care supraîncărcă mai multe argumente sau schimbă numere, puteți utiliza macrocomenzi utilitare pentru a genera automat structuri statice de expediere:

void print_ii(int a, int b) ( printf("int, int\n"); ) void print_di(double a, int b) ( printf("double, int\n"); ) void print_iii(int a, int b, int c) ( printf("int, int, int\n"); ) void print_default(void) ( printf("argumente necunoscute\n"); ) #define print(...) OVERLOAD(print, (__VA_ARGS__), \ (print_ii, (int, int)), \ (print_di, (double, int)), \ (print_iii, (int, int, int)) \) #define OVERLOAD_ARG_TYPES (int, double) #define OVERLOAD_FUNCTIONS (printare) #include „activate-overloads.h” int main(void) ( print(44, 47); // printează „int, int” print(4.4, 47); // printează „double, int” print (1, 2, 3); // afișează "int, int, int" print(""); // afișează "argumente necunoscute" )

(implementare aici). Cu ceva efort, puteți reduce boilerplate pentru a arăta destul de asemănător cu o limbă cu suport de supraîncărcare încorporat.

Deoparte, era deja posibilă supraîncărcarea cantitate argumente (mai degrabă decât tip) în C99.

Rețineți că modul în care este evaluat C vă poate mișca. Acest lucru va selecta foo_int dacă încercați să îi transmiteți un caracter literal, de exemplu, și aveți nevoie de niște foo_int dacă doriți ca supraîncărcările dvs. să accepte literale de șir. Totuși, în general, destul de mișto.

Răspunsul lui Leușenko este foarte grozav: doar exemplul foo nu se compila cu GCC, care eșuează pe foo(7) , lovind PRIMA macrocomandă și apelul de funcție real ((_1, __VA_ARGS__) , rămânând cu o virgulă suplimentară. De asemenea, ne confruntăm cu probleme, dacă vrem să oferim supraîncărcări suplimentare precum foo(double) .

Așa că am decis să răspund la această întrebare mai detaliat, inclusiv permițând supraîncărcarea goală (foo(void) - care a cauzat unele probleme...).

Ideea acum este: definiți mai mult de un generic în diferite macro-uri și lăsați-l pe cel corect să fie ales în funcție de numărul de argumente!

Numărul de argumente este destul de simplu, pe baza acestui răspuns:

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) #define CONCAT(X, Y) CONCAT_(X, Y) # definiți CONCAT_(X, Y) X ## Y

Asta e bine, decidem fie SELECT_1, fie SELECT_2 (sau mai multe argumente dacă vrei/aveți nevoie de ele), așa că avem nevoie doar de definițiile corespunzătoare:

#define SELECT_0() foo_void #define SELECT_1(_1) _Generic ((_1), \ int: foo_int, \ char: foo_char, \ double: foo_double \) #define SELECT_2(_1, _2) _Generic((_1), \ double : _Generic((_2), \ int: foo_double_int \) \)

În primul rând, un apel de macrocomandă gol (foo()) creează în continuare un simbol, dar este gol. Deci macrocomanda de numărare returnează de fapt 1 în loc de 0, chiar dacă macro-ul este numit gol. Putem remedia „cu ușurință” această problemă dacă __VA_ARGS__ cu virgulă după __VA_ARGS__ conditionat, în funcție de dacă lista este goală sau nu:

#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)

Acest privit ușor, dar macro-ul COMMA este destul de greu; din fericire, acest subiect este deja tratat pe blogul lui Jens Gustedt (mulțumesc, Jens). Principalul truc este că macrocomenzile de funcții nu se extind decât dacă sunt urmate de paranteze, vezi blogul Jens pentru explicații suplimentare... Trebuie doar să modificăm puțin macrocomenzile pentru nevoile noastre (voi folosi nume mai scurte și mai puține argumente pentru concizie) .

#define ARGN(...) ARGN_(__VA_ARGS__) #define ARGN_(_0, _1, _2, _3, N, ...) N #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0 ) #define SET_COMMA(...) , #define COMMA(...) SELECT_COMMA \ (\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_COMMA(SET_COMMA __VA_ARGS__), \ HAS_COMMA(SET_COMMA)____ \) #define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) #define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3 # definește COMMA_0000 , #define COMMA_0001 #define COMMA_0010 , // ... (toate celelalte cu virgulă) #define COMMA_1111 ,

Și acum suntem bine...

Cod complet într-un singur bloc:

/* * demo.c * * Creat pe: 2017-09-14 * Autor: sboehler */ #include void foo_void(void) ( puts("void"); ) void foo_int(int c) ( printf("int: %d\n", c); ) void foo_char(char c) ( printf("car: %c) \n", c); ) void foo_double(double c) ( printf("double: %.2f\n", c); ) void foo_double_int(double c, int d) ( printf("double: %.2f, int: %d\n", c, d); ) #define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__) #define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) # definește CONCAT(X, Y) CONCAT_(X, Y) #define CONCAT_(X, Y) X ## Y #define SELECT_0() foo_void #define SELECT_1(_1) _Generic ((_1), \ int: foo_int, \ char : foo_char, \ double: foo_double \) #define SELECT_2(_1, _2) _Generic((_1), \ double: _Generic((_2), \ int: foo_double_int \) \) #define ARGN(...) ARGN_( __VA_ARGS__) #define ARGN_(_0, _1, _2, N, ...) N #define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0) #define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0) #define SET_COMMA(...) , #define COMMA(...) SELECT_COMMA \ (\ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_C OMMA(SET_COMMA __VA_ARGS__), \ HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \) #define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3) #define SELECT_2, ___, SELECT_2, ___3 COMMA_ ## _0 ## _1 ## _2 ## _3 COMMA_1001 , #define COMMA_1010 , #define COMMA_1011 , #define COMMA_1100 , #define COMMA_1101 , #define COMMA_1110 , #define COMMA_1110 , #define charint arv, int 1(define COMMA_ arv, int) ( foo(); foo(7); foo(10.12); foo(12.10, 7); foo((char)"s"); returnează 0; )

Adnotare: Prelegerea discută conceptele, declararea și utilizarea funcțiilor inline și supraîncărcate în programele C++, mecanismele de efectuare a înlocuirii și supraîncărcării de funcții, recomandări pentru îmbunătățirea eficienței programelor prin supraîncărcare sau înlocuire de funcții.

Scopul prelegerii: învățați funcțiile inline (încorporate) și supraîncărcările de funcții, aflați cum să dezvoltați programe folosind supraîncărcarea funcțiilor în C++.

Funcții în linie

Apelarea unei funcții, transmiterea de valori către aceasta, returnarea unei valori - aceste operațiuni necesită destul de mult timp CPU. De obicei, atunci când definește o funcție, compilatorul își rezervă doar un bloc de celule în memorie pentru a-și stoca instrucțiunile. După ce funcția este apelată, controlul programului este transferat acestor operatori, iar la întoarcerea din funcție, execuția programului reia din linia care urmează apelului funcției.

Cu apeluri repetate, de fiecare dată programul va procesa același set de comenzi, fără a crea copii pentru fiecare apel separat.

Fiecare salt în zona de memorie care conține instrucțiunile funcției încetinește execuția programului. Dacă funcția este mică, puteți economisi timp la mai multe apeluri, solicitând compilatorului să introducă codul funcției direct în program de la site-ul apelului. Astfel de funcții sunt numite substituit. În acest caz, vorbind de eficiență, în primul rând, este implicată viteza de execuție a programului.

Funcții în linie sau în linie sunt funcții al căror cod este inserat de compilator direct la site-ul apelului, în loc să transfere controlul la o singură instanță a funcției.

Dacă funcția este inline, compilatorul nu creează funcția dată în memorie, ci copiază șirurile acesteia direct în codul programului de la locul apelului. Acest lucru este echivalent cu scrierea blocurilor adecvate în program în loc de apeluri de funcții. Deci specificatorul în linie defineşte pentru funcţie aşa-numita legarea internă, care constă în faptul că compilatorul înlocuiește comenzile codului său în loc să apeleze funcția. Funcțiile inline sunt utilizate dacă corpul funcției este format din mai multe instrucțiuni.

Această abordare vă permite să creșteți viteza de execuție a programului, deoarece comenzile sunt excluse din program. microprocesor Este necesar pentru a trece argumente și a apela funcția.

De exemplu:

/*funcția returnează distanța de la punctul cu coordonate(x1,y1) la punctul cu coordonate (x2,y2)*/ inline float Linie(float x1,float y1,float x2, float y2) ( return sqrt(pow(x1-x2) ,2)+pow(y1-y2,2)); )

Cu toate acestea, trebuie remarcat faptul că utilizarea funcțiilor inline nu duce întotdeauna la un efect pozitiv. Dacă o astfel de funcție este apelată de mai multe ori în codul programului, atunci în timpul de compilareîn program vor fi inserate atâtea copii ale acestei funcţii câte apeluri sunt. Va exista o creștere semnificativă a dimensiunii codului programului, drept urmare creșterea așteptată a eficienței execuției programului în timp poate să nu aibă loc.

Exemplul 1.

#include „stdafx.h” #include folosind namespace std; inline int Cub(int x); int _tmain(int argc, _TCHAR* argv)( int x=2; float y=3; double z=4; cout<

Enumerăm motivele pentru care o funcție cu specificatorul inline va fi tratată ca o funcție obișnuită non-inline:

  • funcția inline este recursivă;
  • funcții al căror apel este plasat înaintea definiției sale;
  • funcții care sunt numite de mai multe ori într-o expresie;
  • funcții care conțin bucle, comutatoare și operatori de sărituri;
  • funcții care sunt prea mari pentru a permite înlocuirea.

Restricțiile privind efectuarea înlocuirii sunt în mare parte dependente de implementare. Dacă pentru o funcție cu un specificator în linie compilatorul nu poate efectua înlocuirea din cauza contextului în care este plasat apelul la acesta, atunci funcția este considerată statică și este emisă mesaj de avertizare.

O altă caracteristică a funcțiilor inline este imposibilitatea de a le schimba fără a recompila toate părțile programului în care sunt apelate aceste funcții.

Supraîncărcare funcție

La definirea funcțiilor în programe, este necesar să se precizeze tipul valorii returnate de funcție, precum și numărul de parametri și tipul fiecăruia dintre ei. Dacă a existat o funcție în C++ numită add_values ​​care a funcționat pe două valori întregi, iar programul trebuia să folosească o funcție similară pentru a transmite trei valori întregi, atunci ar trebui creată o funcție cu un nume diferit. De exemplu, add_two_values ​​​​și add_three_values ​​​​. În mod similar, dacă doriți să utilizați o funcție similară pentru a lucra cu valori flotante, atunci aveți nevoie de o altă funcție cu alt nume. Pentru a evita duplicarea funcțiilor, C++ vă permite să definiți mai multe funcții cu același nume. În timpul compilării, C++ ia în considerare numărul de argumente utilizate de fiecare funcție și apoi apelează exact funcția necesară. Oferirea compilatorului de a alege între mai multe funcții se numește supraîncărcare.

Supraîncărcarea funcției este crearea mai multor funcții cu același nume, dar cu parametri diferiți. Parametrii diferiți înseamnă ceea ce ar trebui să fie diferit număr de argumente funcțiile și/sau ale acestora tip. Adică, supraîncărcarea funcțiilor vă permite să definiți mai multe funcții cu același nume și tip de returnare.

Se mai numește și supraîncărcarea funcției polimorfismul funcţiei. „Poly” înseamnă mult, „morph” - o formă, adică o funcție polimorfă este o funcție care se distinge printr-o varietate de forme.

Polimorfismul funcției este înțeles ca existența în program a mai multor versiuni supraîncărcate ale unei funcții care au valori diferite. Schimbând numărul sau tipul parametrilor, puteți da două sau mai multe funcții același nume. În acest caz, nu va exista confuzie, deoarece funcția dorită este determinată de coincidența parametrilor utilizați. Acest lucru vă permite să creați o funcție care poate funcționa cu numere întregi, flotanți sau alte tipuri de valori fără a fi nevoie să creați nume separate pentru fiecare funcție.

Astfel, datorită utilizării funcții supraîncărcate, nu ar trebui să vă faceți griji cu privire la apelarea funcției potrivite din program care se potrivește cu tipul de variabile transmise. Când apelați o funcție supraîncărcată, compilatorul va determina automat ce versiune a funcției trebuie utilizată.

De exemplu, următorul program supraîncărcă o funcție numită add_values ​​​​. Prima definiție a funcției adaugă două valori de tip int. A doua definiție a funcției adaugă trei valori de tip int. În timpul compilării, C++ determină corect funcția care trebuie utilizată:

#include „stdafx.h” #include folosind namespace std; int add_values(int a,int b); int add_values ​​(int a, int b, int c); int _tmain(int argc, _TCHAR* argv)( cout<< "200+801=" << add_values(200,801) << "\n"; cout << "100+201+700=" << add_values(100,201,700) << "\n"; system("pause"); return 0; } int add_values(int a,int b) { return(a + b); } int add_values (int a, int b, int c) { return(a + b + c); }

Astfel, programul definește două funcții numite add_values ​​​​. Prima funcție adaugă două valori, în timp ce a doua adaugă trei valori de același tip int. Compilatorul C++ determină ce funcție să folosească pe baza opțiunilor oferite de program.

Utilizarea supraîncărcării funcției

Unul dintre cele mai frecvente cazuri de utilizare pentru supraîncărcare este utilizarea unei funcții pentru a produce un rezultat specific, având în vedere diferiți parametri. De exemplu, să presupunem că programul dumneavoastră are o funcție numită day_of_week care returnează ziua curentă a săptămânii (0 pentru duminică, 1 pentru luni, ... , 6 pentru sâmbătă). Un program ar putea supraîncărca această funcție, astfel încât să returneze corect ziua săptămânii dacă i se dă o zi iuliană ca parametru sau dacă îi este dată o zi, o lună și un an.

int day_of_week(int julian_day) ( // operatori ) int day_of_week(int luna, int zi, int anul) ( // operatori )

Folosind funcții supraîncărcate se fac adesea o serie de erori. De exemplu, dacă funcțiile diferă doar în tipul de returnare, dar nu și în tipurile de argument, astfel de funcții nu pot avea același nume. Următoarea opțiune de supraîncărcare este, de asemenea, invalidă:

int nume_funcție(int nume_argument); int nume_funcție(int nume_argument); /* supraîncărcare nume nevalidă: argumentele au același număr și același tip */

Avantajele supraîncărcării funcțiilor:

  • supraîncărcarea funcțiilor se îmbunătățește lizibilitatea programe;
  • Supraîncărcarea funcțiilor C++ permite programelor să definească mai multe funcții cu același nume;
  • funcții supraîncărcate returnează valori de același tip, dar pot diferi în ceea ce privește numărul și tipul de parametri;
  • supraîncărcarea funcțiilor simplifică sarcina programatorilor, cerându-le să-și amintească doar un nume de funcție, dar apoi trebuie să știe ce combinație de parametri corespunde cărei funcție.

Exemplul 2.

/*Funcțiile supraîncărcate au același nume, dar liste de parametri și valori returnate diferite*/ #include "stdafx.h" #include folosind namespace std; int medie(int primul_numar, int al doilea_numar, int al treilea_numar); int medie(int primul_numar, int al doilea_numar); int _tmain(int argc, _TCHAR* argv)(// funcția principală int număr_A = 5, număr_B = 3, număr_C = 10; cout<< "Целочисленное среднее чисел " << number_A << " и "; cout << number_B << " равно "; cout << average(number_A, number_B) << ".\n\n"; cout << "Целочисленное среднее чисел " << number_A << ", "; cout << number_B << " и " << number_C << " равно "; cout << average(number_A, number_B, number_C) << ".\n"; system("PAUSE"); return 0; }// конец главной функции /*функция для вычисления целочисленного среднего значения 3-х целых чисел*/ int average(int first_number, int second_number, int third_number) { return((first_number + second_number + third_number)/3); } // конец функции /*функция для вычисления целочисленного среднего значения 2-х целых чисел*/ int average(int first_number, int second_number) { return((first_number + second_number)/2); } // конец функции



Se încarcă...
Top