Tutoriale cu indicatori de funcții

Indicatorii de funcții oferă câteva metode extrem de interesante, eficiente și elegante. Aveți posibilitatea să le utilizați pentru a înlocui switch / if-operatori, pentru a pune în aplicare propriile legare târziu sau callbacks. Din păcate, probabil datorită sintaxei complexe, majoritatea cărților de pe computer și documentația pentru ele sunt foarte ostile. Dacă se vorbește, este destul de scurtă și superficială. Ele sunt mai puțin predispuse la erori decât indicii obișnuiți, nu le veți aloca sau elibera memoria pentru ele. Tot ce trebuie să faceți este să înțelegeți ce sunt și să studiați sintaxa lor. Dar rețineți: trebuie să vă întrebați întotdeauna dacă aveți într-adevăr nevoie de un indicator pentru o funcție. Este bine să implementați propria legare ulterioară, dar folosind structurile C ++ existente puteți face codul dvs. mai ușor de citit și mai ușor de înțeles. Un aspect în cazul legării târzii este timpul de execuție: dacă apelați o funcție virtuală, programul dvs. trebuie să determine ce funcție să apeleze. Aceasta se face prin intermediul unei tabele virtuale (tabelul V) care conține toate funcțiile posibile. Fiecare apel durează ceva timp și probabil că puteți scurta timpul utilizând funcții pointeriale în loc de funcții virtuale. Poate că nu.







Când doriți să apelați funcția doit (), la un anumit punct desemnat în eticheta de program, ai pus doar funcția de apel doit () în punctul de tac în codul sursă. Apoi, va compila codul, și de fiecare dată când programul vine la etichete, funcția este numit. Toate bine. Dar dacă nu știi la ce moment ar trebui să fie numit funcția? Ce faceți atunci când doriți să rezolvați acest lucru în timpul execuției? S-ar putea dori să folosească așa-numitul apel invers (funcție de reapelare), sau selectați o opțiune dintr-un grup de funcții posibile. Cu toate acestea, puteți rezolva problema utilizând instrucțiunea switch. unde numiți funcții exact așa cum le doriți, folosind diferite blocuri (ramuri). Dar există un alt mod: utilizați indicatorul funcției!

În următorul exemplu, vom examina sarcina efectuării uneia dintre cele patru operații de bază aritmetice. În primul rând, sarcina este rezolvată folosind instrucțiunea switch. Apoi demonstrează cum se poate face același lucru cu un indicator al funcției. Acesta este doar un exemplu, iar sarcina este atât de simplă încât cred că nimeni nu va folosi un indicator al funcției pentru acest lucru ;-)

Având în vedere sintaxa, puteți selecta două tipuri de pointeri pentru funcții: unul dintre ele este pointeri la funcțiile obișnuite C sau funcțiile statice ale membrilor C ++. Un alt tip este indicatorii pentru funcțiile membrilor non-statici din C ++. Principala diferență este că pentru toate indicatorii pentru funcțiile membrilor non-statici este necesar un argument ascuns: un indicator pentru instanța clasei. Amintiți întotdeauna: Aceste două tipuri de indicii sunt incompatibile între ele.

Deoarece indicatorul unei funcții nu este altceva decât o variabilă. acesta ar trebui definit ca de obicei. În următorul exemplu, definim trei indicatori de funcții: pt2Function, pt2Member și pt2ConstMember. Acestea indică funcții care iau un argument de tip float și două argumente de tip char și returnează un int. În exemplele C ++, se presupune că funcțiile indicate de indicatorii noștri sunt membri (non-statici) ai TMyClass.

În C, apelați o funcție folosind un pointer la o funcție care este dereferențiată în mod explicit prin intermediul operatorului *. Alternativ, puteți utiliza și un indicator pentru a înlocui numele funcției. Există doi operatori în C ++. * și, respectiv, * sunt utilizate împreună cu instanța clasei pentru a apela una dintre funcțiile sale (non-statice) membre. Dacă apelul este efectuat în interiorul unei alte funcții a unui membru, puteți utiliza acest indicator.

Puteți transmite un indicator la o funcție ca parametru de intrare al unei funcții. De exemplu, veți avea nevoie de acest lucru dacă doriți să transmiteți un pointer la funcția de apel invers. Codul de mai jos arată modul de trecere a unui indicator la o funcție care returnează un int. dar are un flotor și două char'a:

3.1 Introducere în conceptul funcțiilor de returnare

Indicatorii de funcții oferă conceptul funcțiilor de apel invers. Dacă nu sunteți sigur despre modul de utilizare a indicatorilor de funcții, reveniți la secțiunea "Introducere la funcții pointeri". Voi încerca să introduc conceptul de funcții de apel invers utilizând funcția de sortare binecunoscută, qsort. Această funcție sortează elementele regiunii în funcție de tipul particularizat. Domeniul de aplicare poate conține elemente de orice tip; este trecut la funcția de sortare prin pointer la void. De asemenea, trebuie transferat dimensiunea elementului și numărul total de elemente din regiune. Acum întrebarea este: cum poate sortarea să sorteze elementele fără informații despre tipul lor? Răspunsul este simplu: funcția obține un pointer la o funcție de comparare care ia indicii void la două elemente din regiune, compară elementele și returnează un rezultat codificat ca int. Astfel, de fiecare dată când algoritmul de sortare cere rezultatul comparării a două elemente, acesta apelează doar funcția de comparare prin intermediul funcției pointer: efectuează un apel invers!







3.2 Cum efectuez un Callback în C?

3.3 Codul exemplului de utilizare qsort

În următoarea cifră, este sortată o serie de elemente plutitoare.

3.4 Cum implementez o provocare inversă într-o funcție a unui membru?

În același mod ca și se face în funcție C. Funcția statică membru nu are nevoie de un obiect pe care l-ar produce, și, astfel, are o semnătură similară cu semnătură C-funcții cu aceeași convenție de asteptare, a solicitat argumente și să se întoarcă de tip.

3.5 Cum se implementează o provocare inversă într-o funcție non-statică a membrilor? Abordarea înfășurării

Pointeri la functii membre non-statice sunt diferite de ukzateley simplu la funcții în C, pentru că au nevoie să treacă printr-un obiect de clasa pointer la anula. Astfel, un simplu indicii de funcții și funcțiile de membru non-statice au diferite și incompatibile semnături! Dacă doriți să se refere la un membru al unei anumite clase, trebuie doar să înlocuiți în codul un indicator simplu pentru un pointer funcție de la o funcție membru. Dar ceea ce se poate face dacă doriți să apelați membru nestatichsky din orice clasă? Acest lucru este oarecum dificil. Trebuie să scrieți o funcție statică ca element de înfășurare. O funcție de memorie statică are aceeași semnătură ca o funcție în C! Apoi, va aduce pointer la obiectul pe care funcția de membru pe care doriți să o apelați, pentru a anula * și trece-l la înveliș ca un parametru suplimentar sau printr-o variabilă globală. Dacă utilizați o variabilă la nivel mondial, este foarte important să ne asigurăm că acesta va indica întotdeauna obiectul corect! Desigur, puteți trece și parametrii de apel la o funcție membră. Wrapper duce la anula pointer la un pointer la un obiect de clasa sootvetstvuyuego și solicită funcția de membru. Mai jos sunt două exemple.

Exemplu A: Un indicator creat într-o clasă este trecut ca parametru suplimentar

Funcția DoItA face ceva cu obiecte din clasa TClassA, care conține un apel invers. Astfel, TClassA pointer la obiectul de clasă și un pointer la înfășurare funcția statică TClassA :: Wrapper_To_Call_Display transferat la DoItA. Acest pachet este o funcție de apel invers. Puteți scrie alte clase arbitrare sunt aceleași ca TClassA și să le utilizeze cu DoItA, atâta timp cât acestea oferă funcțiile necesare. Notă: Această soluție poate fi utilă dacă proiectați propria interfață de apel invers. Este mult mai bine decât a doua soluție care folosește variabila globală.

Exemplul B: Un pointer instanțiat într-o clasă este stocat într-o variabilă globală

Funcția DoItB face ceva cu obiectele TClassA care conțin un apel invers. Pointerul către funcția de învelire statică TClassA :: Wrapper_To_Call_Display este trecut la DoItA. Acest pachet este o funcție de apel invers. Învelișul folosește variabila globală void * pt2Object și efectuează o conversie exactă la un pointer la instanța TClassB. Este foarte important să inițializați întotdeauna variabila globală astfel încât să specificați instanța corectă a clasei. Puteți scrie alte clase arbitrare la fel ca și TClassB și le puteți folosi cu DoItA până când acestea oferă funcțiile necesare. Notă: această soluție poate fi utilă dacă utilizați o interfață de apel invers care nu poate fi modificată. Aceasta nu este o soluție bună, deoarece utilizarea unei variabile globale este foarte periculoasă și poate provoca erori grave.

4.1 Ce sunt functorii?

Functorii sunt funcții cu stare. În C ++, le puteți implementa ca o clasă cu unul sau mai mulți membri privați pentru a stoca starea și cu un operator supraîncărcat () pentru a executa funcția. Functorii pot încapsula indicii la funcțiile C și C ++. folosind conceptele de șabloane și polimorfism. Puteți construi o listă de indicatori pentru funcțiile membrilor claselor arbitrare și le puteți apela pe toate prin aceeași interfață, fără a vă îngrijora de clasa lor sau de necesitatea unui indicator pentru instanța clasei. Numai în acest scop toate funcțiile ar trebui să aibă același tip de retur și parametri de apel. Uneori functorii sunt cunoscuți ca închizători. De asemenea, puteți utiliza functorii pentru a efectua apeluri.

4.2 Cum se implementează functorii?

În primul rând, aveți nevoie de o clasă de bază numită TFunctor care oferă o funcție virtuală denumită Call sau un operator virtual supraîncărcat (), cu care puteți apela o funcție membră. Ce preferați, un operator sau o funcție supraîncărcată, cum ar fi Call, depinde de dvs. Din clasa de bază, moșteniți clasa de șabloane TSpecificFunctor, care este inițializată de un constructor care ia un pointer la un obiect și un pointer la o funcție membru. Clasa derivată înlocuiește funcția de apel și / sau operatorul de clasă de bază () în versiunea suprascrisă, numiți funcția membră utilizând indicatorii memorați la obiect și funcția membru. Dacă nu sunteți sigur cum să utilizați indicatorii de funcții, consultați introducerea la instrucțiunile de funcționare.

4.3 Exemplu de utilizare a functorilor

În exemplul următor avem două săli de clasă, acestea oferă o funcție denumită „display“, care returneaza nimic (void), și necesită un șir de caractere (char const *) ca parametru de intrare. Vom crea o serie de două indicii TFunctor și inițializa-l cu două indicatoare către TSpecificFunctor, care încapsulează pointeri la obiecte și indicii pentru membri și, respectiv, TClassA TClassB. Apoi folosim o serie de functori pentru a apela funcțiile corespunzătoare ale membrilor. Pentru a efectua un apel de funcție, un pointer la obiectul nu este necesară și nu trebuie să vă faceți griji mai multe despre clase!

5.1 Introducerea indicatorilor de funcții

5.2 Comenzi rapide și funcții

Secțiunea 3.5. Exemplul A

În acest exemplu, indicatorul la conversia încurcă TClassA clase de obiecte de a anula * DoItA în funcția de transmisie și transformare apoi invers cursorul pe TClassA funcție înveliș TClassA :: Wrapper_To_Call_Display (același lucru se aplică în exemplul B). modele similare sunt potrivite (pentru că așa cum alții nu au de ales), dacă doriți să lucrați cu caracteristici, cum ar fi pthread_create () (libpthread.so), care ia ca parametri de intrare void *. În orice caz, acest exemplu va funcționa corect numai dacă un pointer către TClassA este transferat de fapt către DoItA. Dar ce se întâmplă dacă, din greșeală, altceva este transferat în locul unui indicator la TClassA? Compilatorul nu recunoaște eroarea, nimic nu va împiedica convertit pentru a anula * pointer la int, de exemplu. Pentru claritate, am modificat ușor exemplul (exemplul A.cpp):
  • în clasa TClassA au fost adăugați doi membri de tip int - a și x,
  • funcția TClassA :: Afișajul afișează valoarea x,
  • în funcția Callback_Using_Argument () sunt create două variabile, primul este un obiect al clasei TClassA, celălalt este de tip int; Apoi, pentru ambele, se numește funcția DoItA.
Nu este greu de ghicit că funcția DoItA, primind ca intrare un pointer la int, se face, să-l puneți blând, nu este cum era de așteptat. În loc de valoarea TClassA :: x apare nesigur set de biți, să-l puneți pur și simplu - gunoi. O posibilă soluție la această problemă - do Wrapper_To_Call_Display DoItB și funcțiile șablon (exampleB.cpp). Deoarece int nu are o specializare adecvată, compilatorul va afișa un mesaj de eroare.

Realizat de uCoz







Trimiteți-le prietenilor: