Interfață necunoscută

Deci, este găsită decizia cu privire la ceea ce lipsește încă obiectul nostru de server, astfel încât clientul să se poată descurca în mod normal. Au rămas câteva detalii despre proiectarea acestei soluții în proiectarea exactă a programului. Am stabilit că avem nevoie de un dispozitiv pentru conversia tipurilor de pointeri în interfețe și de dispozitivul pentru calcularea referințelor obiectului, cu un "auto-lichidator" integrat. Acestea pot fi accesate de client doar prin metodele obiectului pe care clientul îl apelează, iar aceste metode trebuie fie să fie în structura fiecărei interfețe implementate de obiect sau sunt decorate cu o interfață specială separată. Și absolut orice obiect, indiferent de fel și trib este - este obligat să realizeze această funcționalitate.







Având în vedere că prezintă aceste caracteristici pot fi doar ca o interfață, și subiectul de discuție: ceea ce este mai bine - să le elibereze sub forma unei singure interfețe, obligația de a expune faptul că la impute orice obiect (inclusiv toate celelalte interfețe obiect), sau - pentru a adăuga această funcționalitate pentru fiecare interfață, obiect expus ?

Stabiliți despre ce vorbim. Prima metodă va fi numită QueryInterface - trebuie să accepte ID-ul unei alte interfețe expuse de același obiect și să returneze un pointer la această interfață. A doua metodă va fi numităAddRef - nu are parametri și fiecare apel la aceasta duce la avansarea numărătorului de referință al obiectului înainte de unul. A treia metodă - Refaceți. Sarcina sa este inversăAddRef, iar atunci când numărul de referință ajunge la zeroRelease, acesta va apela și șterge acest lucru.

De ce am venit cu două în locul uneia dintre metodele de gestionare a numărului de referință? Cel puțin pentru că codul pentru a apela o metodă fără parametri este mai scurt. Hai cu câțiva octeți, dar acești câțiva octeți vor fi în client să se întâlnească peste tot unde avem un pointer. Și aditivul total la cod poate fi mare.

Deci, aceste trei metode:

putem emite o interfață separată. Sau - ne putem înregistra în fiecare interfață. Care este mai bine? Și de ce?

Să presupunem că această funcție este afișată într-o interfață complet separată X, iar toate celelalte interfețe ale aceluiași obiect nu o au. Ce se va întâmpla? Ceea ce se întâmplă este dacă cerem serverului să returneze un pointer la interfața X atunci când creează obiectul. atunci, deținând acest pointer, obținem cu ușurință indicii și pe toate celelalte interfețe ale obiectului -QueryInterface este situat în interfața X. Dar dacă cerem serverului să returneze orice altă interfață a aceluiași obiect, atunci cu această interfață vom rămâne - în această interfață nu există nici o interfață QueryInterface. Acest lucru ne face mereu să cerem interfața de care nu avem nevoie, și anume X. și apoi de la ea deja produce pointer dorit. Există o dublă lucrare pe partea clientului - când primiți un pointer la interfață.

Dacă această funcționalitate este plasată în interiorul oricărei interfețe care prezintă obiectul, atunci fiecare interfață suplimentară va fi ocupată de trei celule Vtbl suplimentare. dar nu este nevoie să efectuăm nici o lucrare dublă pe partea clientului, toate gestionarea obiectului se poate face prin orice interfață. Și cumva pare că a doua cale este mult mai convenabilă din partea clientului, desigur - serverul este folosit din nou.

Dar, de fapt, este posibil să construim și un astfel de obiect la care să nu existe o interfață "utilă"? Poți. Dar această funcționalitate a serviciului ar trebui să fie în orice caz. Și puteți multiplica pointerul, apoi distrugeți obiectul. Și tot ce este dincolo de asta - este determinat numai de esența sarcinii care este rezolvată de programator.







Funcționalitatea descrisă este atât de fundamentală încât fără ea "nimic nu funcționează deloc" - ne-am gândit la asta, deoarece în implementarea noastră a interacțiunii componente nu au fost suficiente fragmente esențiale. Este posibil să o implementăm în mod diferit? În detaliu - da, de fapt - nu. La urma urmei, motivul prezenței acestei funcționalități în compunerea obiectului este filozofic. Dacă am ști exact compilator tipul static al subiectului și acest tip a fost unul pentru client și server, desigur, compilatorul ar putea da seama si corecta apelul la noi și corecte vyzovdelete *, iar compilatorul ar converti indicii de tip. Dar, de fapt, aceasta înseamnă că atât clientul și serverul trebuie să fie plasate în cadrul aceluiași proiect - și tocmai am avea exact opuse „condițiile inițiale“. La noi, atât clientul, cât și serverul trebuie să se stabilească în mod diferit în diferite proiecte, în diferite contexte. La noi, la toate, programarea de la componente binare.

Este în vedere acest lucru, am luat mai întâi tabele piesa compilare încorporat în obiectul în sine, astfel încât acestea să fie menținute și run-time (vtbl), iar acum avem nevoie pentru a încărca un obiect și caracteristici, cum ar fi managementul timpului și exprimate de viață. Și nu putem evita acest lucru, fie noi, fie compilatorul.

Ar trebui remarcat în mod special faptul că, deși studiem COM. ceea ce este formulat acum are un caracter filosofic. Și, prin urmare, într-o formă sau alta pot fi găsite în punerea în aplicare a CORBA și ar trebui să fie, în general, în orice tehnologie componentă binar. "Înfășurarea" acestui lucru poate fi diferită, dar esența este aceeași.

În COM, acest lucru este într-adevăr esența fundamentală numit „interfeysIUnknown“, care vag tradus poate suna ca „interfață necunoscută“ și este de cel puțin oarecum derutat - ceea ce este necunoscut interfață, în cazul în care este garantat de prezența oricărui obiect? Cu toate acestea, dacă aceasta este tradusă ca interfața "Necunoscută necunoscută", totul intră în vigoare. În plus, obiectul component binar va fi un obiect COM dacă și numai dacă implementează cel puțin interfața IUnknown. Dacă nu există o astfel de interfață, acesta nu este un obiect COM. deși, așa cum am văzut deja, un obiect poate fi "component binar" și fără a utiliza COM.

Cu această interfață, trebuie să cunoaștem foarte îndeaproape - este "alfa și omega" a tuturor interfețelor, el determină comportamentul special al obiectului. Și acum, pentru moment, vom nota - orice interfață COM ar trebui să fie moștenită de la interfața IUnknown. Ar trebui să fie clar de ce - exactIu necunoscut în compoziția oricărei interfețe oferă control asupra duratei de viață a obiectului și reducerea tipului de pointer. Și pentru aceasta, nu sunt necesare costuri suplimentare pentru clienți.

De fapt, doar introducerea obiectelor din exemplul nostru anterior de implementare IUnknown ne-a separat obiectele de transformarea în "obiecte reale ale COM". Dar pentru a trece mai departe, trebuie să ne cunoaștem exact specificația - ce este "interfața COM" în C ++ și cum este descris.

O astfel de situație nu poate fi "controlată automat" deloc. Evitați aceasta poate fi numai codarea a tot ceea ce este expus la exteriorul modulului. Este necesar să ne amintim constant că "numele extern normal" al C ++ este decorat, adică În modulul binar nu arată ca în interiorul textului sursă. Trebuie reținut că orice metodă extern expusă trebuie scrisă numai în acordul de legătură .stdcall. Trebuie reținut că metodele interfețelor expuse nu pot fi supraîncărcate. Aceste restricții urmează doar pentru că "alți compilatori nu știu cum", iar COM este o tehnologie binară.

Prin urmare, cunoașterea exactă a "felului de construcție a limbajului este o interfață în COM" pentru un programator este o necesitate absolută. În C ++, fiecare interfață este descrisă de o structură - o structură este o clasă, toți membrii ei fiind publici. Nu este exclusă și descrierea interfeței însăși este o clasă de proiectare. În fișierul inclus Există astfel de definiții ale structurilor pentru definirea părților interfeței:

#define STDMETHODCALLTYPE __stdcall

#define struct interface

#define STDMETHOD (metoda) metoda virtuală HRESULT STDMETHODCALLTYPE

#define STDMETHOD_ (tip, metoda) metoda virtuala STDMETHODCALLTYPE

# definește interfața DECLARE_INTERFACE (iface) iface

#define interfața DECLARE_INTERFACE_ (iface, baseiface) iface. publice

dar într-un dosar :

typedef LONG HRESULT;

În dosar (cu câteva abrevieri și simplificări), interfața cunoscută nouă este descrisă ca fiind:







Articole similare

Trimiteți-le prietenilor: