Ada (limbaj de programare)

De la Wikipedia, enciclopedia liberă.
Salt la navigare Salt la căutare
Ada
limbaj de programare
Autor
Data de origine 1980
Utilizare scop general
Paradigme
Tastare
Extensii comune .adb .ads
Influențată de ALGOL 68 (Ada 83), Pascal (Ada 83), C ++ (Ada 95), Smalltalk (Ada 95), Java (Ada 2005)
A influențat C ++ , Eiffel , PL / SQL , VHDL , Ruby , Java , Seed7
Implementare referință
Site-ul web www.adaic.org

Ada este un limbaj de programare dezvoltat la sfârșitul anilor 1970 la inițiativa Departamentului Apărării al Statelor Unite (DOD). Atât specificațiile, cât și dezvoltarea limbajului au fost încredințate cererilor de oferte. Printre cele 17 propuneri trimise în urma licitației lansate de DOD, proiectul lui Jean Ichbiah , care la acea vreme lucra la CII Honeywell Bull , a fost ales în 1979 . Specificația a devenit un standard ANSI și ISO în 1983 , urmată de revizuiri ulterioare în 1995, 2005 și 2012. Un subset greu în timp real al limbajului este cunoscut sub numele de profilul Ravenscar . [1]

Ada combină principiile și tehnicile din diferite paradigme de programare , în special programarea modulară , programarea orientată pe obiecte , programarea concurentă și calculul distribuit . Deși interesul DOD a fost în principal în dezvoltarea aplicațiilor militare, Ada este un limbaj de uz general care se pretează să fie utilizat în orice domeniu de aplicație . Cu toate acestea, originea militară este dezvăluită în prezența caracteristicilor puternic orientate spre securitate ale codului; din acest motiv, limbajul este folosit și astăzi în multe contexte în care funcționarea corectă a software-ului este critică, cum ar fi astronautică , avionică , controlul traficului aerian , finanțe și dispozitive medicale. [2] [3] [4]

Compilatoarele Ada utilizate pentru dezvoltarea de software-uri critice pentru misiune trebuie să urmeze un proces de certificare conform standardului internațional ISO / IEC 18009 ( Ada: Conformity Assessment of a Language Processor ), implementat în Ada Conformity Assessment Test Suite (ACATS), parte integrantă la procesul de certificare desfășurat de laboratoare autorizate de Ada Compiler Assessment Authority (ACAA).

Numele inițial al limbajului trebuia să fie DOD-1 , dar mai târziu a fost schimbat în Ada în onoarea lui Ada Lovelace , un ilustru matematician de la începutul secolului al XIX-lea, creditat ca primul programator din istorie pentru dezvoltarea unui algoritm pentru calcularea numerelor de către Bernoulli pe motorul analitic al lui Charles Babbage .

Caracteristici

Ada moștenește câteva caracteristici stilistice fundamentale de la ALGOL , cu privire la care adaugă multe caracteristici de bază (cum ar fi sistemul de tipuri , înregistrări , indicatori sau enumerări , implementat în mare parte în stil Pascal ) și caracteristici avansate tipice limbajelor de programare moderne ( polimorfism , moștenire , excepții , sarcini ).

Limbajul oferă un număr mare atât de verificări statice (timp de compilare), cât și dinamice (timp de execuție ), care previn o mare varietate de erori (utilizarea incorectă a parametrilor, erori de tip, încălcarea intervalului și off-by-one) ). Comenzile dinamice pot fi dezactivate dacă doriți să maximizați eficiența, prin pragma Suppress , [5] sau prin comutatoare specifice ale diferitelor compilatoare. Securitatea și fiabilitatea codului sunt de fapt unul dintre principalele aspecte care au ghidat dezvoltarea limbajului. [6]

"Salut Lume!" în Ada

O lume Hello în Ada este următoarea: [7]

 cu Ada.Text_IO ; folosiți Ada.Text_IO ;
procedura Bună ziua este
începe
  Put_Line ( "Bună ziua, lume!" );
sfârșit Bună ziua ;

Sintaxă

Spre deosebire de majoritatea limbajelor de programare, Ada nu distinge majuscule și minuscule . [8] Cele mai frecvente convenții stilistice implică scrierea cuvintelor cheie lingvistice în totalitate cu litere mici (sau, mai rar, în majuscule) și numele tuturor identificatorilor (tipuri, variabile, subrutine, pachete etc.) în cazuri de șarpe cu l inițială a fiecărui cuvânt capital. Comentariile sunt cu o singură linie, începând cu o liniuță dublă -- și se termină cu o întoarcere cu trăsura. Pentru identificatori și comentarii, Ada acceptă setul de caractere ISO 10646 , permițând astfel scrierea codului în caractere care nu sunt ASCII , iar lungimea maximă permisă pentru identificatori este legată de implementare (în implementările standard trebuie să fie egală cu cel puțin 200 de caractere) . [9]

Sintaxa Ada folosește câteva simboluri, preferând utilizarea cuvintelor în limba engleză. Simbolurile sunt atribuirea := , operatorii aritmetici elementari ( + , - , * , / și ** ), operatorii de comparație ( < , <= , > , >= , = , /= ), colonul : să declare tipul unei variabile, săgeata => în agregate, virgula , ca separator în agregate, operatorul de acces la câmp . , indicația intervalului .. , bifați ' pentru a accesa atributele, caseta <> pentru a indica un parametru variabil în declarații, punctul și virgula ; pentru a separa parametrii unei rutine și pentru a termina afirmațiile. Instrucțiunea nulă constă din cuvântul cheie null terminat de punct și virgulă, în timp ce o linie goală terminată de punct și virgulă nu este validă. Restul operatorilor sunt implementați prin cuvinte cheie ( and , or , rem , mod etc.).

Ada este un limbaj structurat , iar blocurile sunt delimitate de cuvinte cheie. Închiderea blocului conține o referință sau un identificator pentru a înțelege imediat căruia îi aparține deschiderea și pentru a evita confuzia, prevenind, de asemenea, potențialele legături sau alte erori similare. De exemplu, un subrutină care conține o buclă for arată ca:

 procedura Exemplul este
  X : matrice ( domeniul întreg 1 .. 5 ) al întregului ;
începe
  pentru I în bucla X ' Range
    X ( I ) : = I ;
  bucla de capăt ;
end Exemplu ;

Cuvinte rezervate

Limba, în raport cu standardul Ada 2012, are 73 de cuvinte rezervate : [10]

 avorta
abs
abstract
Accept
acces
aliased
anexă
și
matrice
la
începe
corp
case
constant
declara
întârziere
delta
cifre
do
altceva
elsif
Sfârșit
intrare
excepție
Ieșire
pentru
funcţie
generic
mergi la
dacă
în
interfață
este
limitat
buclă
mod
nou
nu
nul
de
sau
alții
afară
primordial
pachet
pragma
privat
proceduri
protejat
a ridica
gamă
record
rem
redenumește
requeue
întoarcere
verso
Selectați
separa
niste
subtip
sincronizat
etichetat
sarcină
terminat
atunci
tip
pana cand
utilizare
cand
in timp ce
cu
xor

Există o omonimie între cuvintele rezervate access , delta , digits , mod și range și atributele omonime, dar din moment ce atributele sunt întotdeauna precedate de un apostrof (numit tick ) nu există riscul de ambiguitate. [11]

Declarații variabile

Declarațiile variabile în Ada sunt făcute prin indicarea numelui entității declarate, urmat de : și apoi de tip, acesta din urmă precedat de orice specificatori ( constant , aliased , not null etc.). O sămânță statică sau dinamică poate fi opțional indicată după tip, separată prin := , iar declarația este finalizată prin ; . Declarațiile pot fi plasate în caietul de declare sarcini al unui pachet sau în partea declarativă a unei rutină sau bruiaj declare . Mai multe variabile pot fi declarate în aceeași declarație, separând numele cu virgule, dar este doar zahăr sintactic și din punct de vedere tehnic declarațiile sunt încă distincte și independente una de cealaltă, astfel încât, de exemplu, o posibilă expresie de inițializare este evaluată o dată pentru fiecare variabilă. [12]

Este posibil să se definească variabile constante precedând tipul de cuvânt cheie constant , caz în care este obligatoriu să se furnizeze o inițializare și este, de asemenea, posibil să se definească constante numerice prin omiterea tipului înainte de inițializare, care trebuie să conste dintr-o expresie statică , care este cunoscut pentru a compila timpul (tipul constantei este dedus din inițializatorul literal). Diferența dintre variabilele constante și constantele numerice este că primele sunt variabile calculate la runtime, în timp ce cele din urmă sunt rezolvate la compilare. [13]

 - variabilă întreagă
N : întreg ;

- două variabile întregi, inițializate cu o valoare aleatorie
- (pot lua două valori diferite)
I , J : Număr întreg : = Aleatoriu ( semințe );

- variabilă constantă (evaluată la runtime)
F : Float constant : = Float ( I + J );

- constantă numerică (rezolvată static la momentul compilării)
π : constantă : = 3,14159_26535_89793_23846 ;

Mergi la

Spre deosebire de multe limbi moderne, Ada păstrează declarația goto . Deși, în general, este depreciat în programarea structurată , este de fapt o instrucțiune utilă pentru generarea automată de cod pornind de la surse în alte limbi sau specificații formalizate la nivel înalt. Este, de asemenea, acceptabil în anumite contexte, cum ar fi sărind spre sfârșitul unui bloc [14] (de exemplu, o iterație a unei bucle) sau ieșirea din bucle adânc imbricate, [15] unde este mai ușor de citit decât continue similare break sau continue în alte limbi ca punctul final al saltului în cod este clar indicat de etichetă (în timp ce break în limbi precum C poate fi predispusă la erori ). [16]

 procedura Print_Integers este
începe
  pentru I în 1 .. 20 de bucle
    Pune (Integer 'Image (I));
    dacă am mod 5 = 0 atunci
      New_Line ;
      du-te Continuă ; - mergi la
    incheie daca ;
    Put ( "," );
  <<Continue>> - etichetă
  bucla de capăt ;
end Print_Inters ;

Tip sistem

Tastați ierarhia în Ada

Ada este un limbaj puternic tastat și sigur . [17] Spre deosebire de C și derivatele sale, Ada nu permite conversii de tip implicite (cu excepția tipurilor universale și a tipurilor de acces anonime) [18], iar conversia de tip se face scriind numele tipului urmat de variabilă pentru a converti între paranteze [19] , de exemplu:

 Conversia este procedură
  X : Float : = 2,9 ;
  Y : întreg ;
începe
  Y : = X ; - ilegal (atribuiți valoarea Float variabilei Întreg)
  X : = X + 1 ; - ilegal (operatorul + nu este definit pentru tipurile mixte)
  X : = X + 1,0 ; - legal
  Y : = Întreg ( X ); - conversie tip (cu rotunjire): acum Y este 3
încheie conversia ;

Tipul unui literal poate fi indicat în mod explicit cu o sintaxă similară cu conversia adăugând o bifă înainte de paranteze de deschidere. Acest lucru este util mai ales în caz de ambiguitate, atunci când același literal (cum ar fi un șir sau o valoare enumerativă) se poate referi la diferite tipuri. [20]

Tipuri predefinite

Spre deosebire de C și derivatele sale, nu există cuvinte cheie în Ada care să identifice tipurile primitive, iar toate tipurile predefinite sunt definite în biblioteca standard. Tipurile de bază predefinite (cum ar fi Integer , Float sau Character ) sunt definite în pachetul Standard , dar pentru a maximiza portabilitatea, se consideră o bună practică să nu utilizați direct tipurile predefinite și să le definiți în schimb, chiar și pentru cele mai simple tipuri numerice. [21]

Tipurile definite în pachetul Standard sunt întotdeauna vizibile în fiecare parte a programului, în timp ce celelalte tipuri definite în pachetele rămase ale bibliotecii standard necesită o clauză with pentru a fi vizibile. Tipurile definite în Standard sunt: [22]

  • Integer : tip discret Integer , care ia valori într-un interval de extensie legat de implementare, nu mai puțin de .. .
    • Natural : subtip de Integer care poate presupune doar valori non-negative;
    • Positive : subtip care poate presupune doar valori pozitive nenule;
  • Float : tip cu virgulă mobilă cu cel puțin șase cifre;
  • Duration : tip punct fix, folosit pentru a exprima un timp în secunde;
  • Character , Wide_Character , Wide_Wide_Character : tipuri speciale de enumerare, utilizate pentru caractere text codate pe 8, 16 și respectiv 32 de biți (ultimele două au fost adăugate în Ada 95 și respectiv Ada 2005)
  • String , Wide_String , Wide_Wide_String : șiruri de lungime fixă, constând din tablouri de caractere. Alte două varietăți de șiruri, mai flexibile, dar și mai grele din punct de vedere al calculului, sunt definite în alte pachete ale bibliotecii și sunt Ada.Strings.Bounded.Bounded_String (pentru șiruri cu lungime variabilă până la o valoare maximă) și Ada.Strings.Unbounded.Unbounded_String (pentru șiruri fără restricții de lungime variabilă), ale căror pachete respective oferă și rutine de manipulare;
  • Boolean : tip enumerativ care poate asuma valorile False și True și care are o anumită semantică.

Pachetele System și System.Storage_Elements oferă câteva tipuri care sunt utile pentru programarea la nivel scăzut: [23]

  • System.Address : reprezintă o adresă de memorie;
  • System.Storage_Elements.Storage_Offset : reprezintă un offset de memorie, care poate fi adăugat sau scăzut dintr-o adresă pentru a obține o altă adresă;
  • System.Storage_Elements.Storage_Count : un subtip Storage_Offset care poate lua doar valori non-negative, folosit pentru a reprezenta dimensiunea unei structuri de date;
  • System.Storage_Elements.Storage_Element : reprezintă unitatea minimă de memorie adresabilă (adică un singur octet în majoritatea implementărilor);
  • System.Storage_Elements.Storage_Array : matrice de Storage_Element .

Declarație de tipuri și subtipuri

Tipurile pot fi definite utilizând type cuvântului cheie, sub forma unei enumerări, ca tip cu virgulă mobilă cu cuvântul cheie cu digits sau pornind de la un alt tip preexistent, utilizând cuvântul cheie new , în timp ce atributul 'Base unui tip oferă tipul de bază din care derivă. Pornind de la un tip este posibil să se definească un subtip (cu subtype cuvântului cheie), care constă dintr-un set de valori conținute în domeniul tipului din care derivă. [24]

 - nou tip numeric definit pornind de la Integer
tipul Ziua este nou întreg ;

- subtipul zilei
subtipul Day_Of_Month este intervalul Day 1 .. 31 ;

Tipuri enumerative

Tipurile enumerative pot fi specificate prin indicarea valorilor posibile între paranteze. Literalele de tip enumerativ pot intra în conflict și, în acest caz, tipul este specificat în mod explicit pentru a rezolva ambiguitatea. Este posibil să se specifice o serie de valori enumerative și este posibil să se declare subtipuri enumerative care iau doar o parte din valorile tipului din care derivă.

Operatorii de comparație sunt definiți automat pentru tipurile enumerative (ordinea este dată de poziția din declarație, începând de la valoarea minimă spre maximă) și au mai multe atribute utile, printre care 'First și 'Last , care returnează primul și ultima valoare de tip, 'Succ și 'Pred , care returnează valorile anterioare și următoare, 'Pos și 'Val , care returnează indicele pozițional (bazat pe zero) al valorii (adică poziția din declarație) respectiv și valoarea asociată unui index. [25]

 procedurile P este
  tipul Divinitatea este ( Jupiter , Juno , Minerva , Eros , Venus );
  tip Planeta este ( Mercur , Venus , Pământ , Marte , Jupiter , Saturn , Uranus , Neptun );
  subtipul Rocky_Planet este gama Planet Mercur .. Marte ;

  P1 : Planeta : = Mercur ;
  D1 : Divinitate : = Juno ;

  - literali ambigui
  P2 : Planeta : = Planeta ( Jupiter );
  D2 : Divinitate : = Divinitate '( Jupiter );

  B : Boolean ;
  I : Întreg ;
  D : Divinitate ;
  P : Planeta ;
începe
  - exemple de operatori și atribute
  B : = Juno < Minerva ; - Adevărat
  I: = Planeta 'Pos (Venus); - 1
  P : = Rocky_Planet ' Last ; - Marte
  D : = Divinity ' Pred ( Minerva ); - Juno
capătul P ;

Matrice

Matricile sunt definite utilizând array cuvinte cheie, specificând între paranteze natura indicilor (care pot fi de orice tip discret). În plus față de tip, este posibilă specificarea unui interval pentru indicii matricei, indicând nu dimensiunea structurii, ci o pereche de valori minime și maxime pentru index, eliminând la rădăcină problema alegerii între zero și unul. Elementele unui tablou sunt accesate prin marcarea indexului între paranteze rotunde. [26]

Un tip de matrice poate fi definit anonim în declarația variabilei sau un tip specific de matrice poate fi declarat cu cuvântul cheie type . Diferența fundamentală este că, în primul caz, două matrice nu pot fi atribuite reciproc, chiar dacă sunt declarate cu un tip anonim care arată la fel, deoarece sunt formal două tipuri diferite (chiar dacă declarația are loc în aceeași linie prin separarea numele variabilelor cu virgulă, deoarece această notație este doar zahăr sintactic pentru două declarații distincte). [27]

Tablourile acceptă tranșarea, atribuind simultan o gamă (nu neapărat statică) de indici, chiar și cu elemente suprapuse. [28]

 procedurile P este
  - matrice de tip anonim
  A , B : matrice ( domeniul întreg 1 .. 5 ) de Float ;
  
  - tip pentru matrice, cu lungime variabilă
  tip Vector este matrice ( interval întreg <>) de Float ;
  - subtip pentru matrice de lungime fixă
  subtipul Vector_5 este Vector ( 1 .. 5 );

  - matrice cu index 1..5
  C , D : Vector_5 ;

  - matrice cu index 1..6
  E : Vector ( 1 .. 6 );
începe
  A : = B ; - ilegală (nu compilează), cele două variabile au tipuri diferite (anonime)
  C : = D ; - legal
  C : = E ; - ilegal

  A ( 1 .. 3 ) : = B ( 2 .. 4 ); - felie
  A ( 1 .. 3 ) : = A ( 2 .. 4 ); - felie cu element suprapus (ok)
capătul P ;

Este posibil să se declare matrice multidimensionale native, separând indexurile cu virgule, sau sub forma unei matrice de matrice (și, prin urmare, matrice zimțate ), în acest caz având grijă să acordăm atenție ordinii declarațiilor. [29]

 procedurile P este
  A : matrice ( interval întreg 1 .. 5 , interval întreg 2 .. 7 ) de Float ; - matrice de dimensiunea 2
  B : matrice ( interval întreg 2 .. 7 ) de matrice ( interval întreg 1 .. 5 ) de float ; - matrice de matrice
începe
  A ( 1 , 2 ) : = 5,0 ; - acces la un element dintr-o matrice multidimensională
  B ( 1 ) ( 2 ) : = 5,0 ; - acces la un element dintr-o serie de matrice
capătul P ;

Pentru tablouri, puteți utiliza literale (numite agregate) în declarații sau instrucțiuni, constând dintr-un set de valori între paranteze, separate prin virgule. Agregatele pot fi poziționale sau nominative: în primul caz, corespondența valorii cu indicele la care se referă este dată de poziția din agregat (ordinea valorilor trebuie să urmeze deci cea a elementelor matricei ), în al doilea caz în loc pentru fiecare valoare, specificați indexul, folosind simbolul => pentru a-l separa de valoare. Este posibil să atribuiți aceeași valoare mai multor indici, utilizând intervalul .. dacă acestea sunt consecutive sau sunt o conductă | pentru a enumera valori non-consecutive. Sintaxa agregatelor este strictă și nu permite inițializări parțiale, dar este posibilă specificarea individuală a unor elemente și completarea atribuirii cu o valoare pentru toate elementele rămase, utilizând cuvântul cheie others . [30]

 - agregat pozițional
A : matrice ( domeniul întreg 1 .. 5 ) a întregului : = ( 1 , 2 , 3 , 4 , 5 );

- agregat nominativ, cu interval și altele (agregatele pentru matricile imbricate sunt poziționale)
B : matrice ( interval întreg 2 .. 7 ) de matrice ( interval întreg 1 .. 5 ) de număr întreg
  : = ( 2 .. 4 | 6 => ( 1 , 3 , 5 , 7 , 9 ), altele => ( 2 , 4 , 6 , 8 , 10 ));

Puteți aplica operatorii not , and , or și xor la matrici unidimensionale de valori Boolean de același tip și lungime, iar rezultatul este un tablou ale cărui componente sunt calculate prin aplicarea operatorului la perechile de componente individuale ale operanzii matrice. [31]

Record

Înregistrările pot fi definite cu cuvântul cheie record , specificând câmpurile în același mod ca și declarațiile variabile (inclusiv posibilitatea de a atribui valori implicite). Este posibil să accesați câmpurile unei înregistrări cu notație punct și să utilizați agregate construite similar cu cele ale matricelor. De asemenea, puteți defini o înregistrare fără câmpuri cu perechea de cuvinte cheie cu null record , care este utilă pentru definirea tipurilor abstracte sau pentru extinderea unui tip fără a adăuga câmpuri noi. [32]

 procedurile P este
  - record
  tip Date este
    record
      Ziua : intervalul întreg 1 .. 31 : = 1 ;
      Lună : Nume_Lună : = ianuarie ; - Name_Month este un tip de enumerare
      An : întreg : = 1970 ;
    înregistrare finală ;

  D , E : Data ;

  tipul Null_Record este înregistrare nulă ; - înregistrări fără câmpuri
începe
  D: = (10 ianuarie 1995);
  Și. Ziua: = D. Ziua ;
capătul P ;

Tipuri parametrice

Puteți utiliza parametri (numiți discriminanți) în declarația unui tip, care va fi specificat atunci când se declară variabile sau se derivă un tip non-parametric. [33]

 pachet Discriminanți este
  - tastați pentru un text de lungime generică, specificat de dimensiunea discriminantă
  tip Text ( Dimensiune : Pozitiv ) este
    record
      Poziție : pozitivă : = 1 ;
      Date : Șir ( 1 .. Dimensiune );
    înregistrare finală ;

  - variabilă pentru un text de 100 de caractere
  T : Text ( 100 );
sfârșitul Discriminant ;

Tipuri limitate

Un alt mecanism de control al tipului este dat de posibilitatea de a defini tipuri limitate, utilizând cuvântul cheie limited . Tipurile restricționate au doar operațiunile declarate în pachet, deci nu pot fi atribuite cu := și nu au operațiile de comparație implicite = și /= . Un tip privat poate fi definit ca limitat, dar implementat intern ca nelimitat, în acest caz tipul este limitat în afara pachetului, dar nu este limitat în partea privată a acestuia, asigurând flexibilitate în cadrul pachetului, acolo unde operațiunile predefinite rămân disponibile și în același timp, controlul asupra tipului față de cei care îl utilizează în afara ambalajului. [34]

Modularitate

Ada este un limbaj structurat și vă permite să împărțiți codul în mai multe unități de compilare (rutine și pachete), care pot fi compilate separat.

Rutină

Ada acceptă două tipuri de rutine , funcții și proceduri, declarate cu cuvintele cheie function și procedure . [35] Primele au o valoare de returnare și pot fi utilizate numai în contextul unei expresii (în Ada, spre deosebire de limbile derivate din C, nu există declarații de expresie ), în timp ce acestea din urmă nu au valoare de returnare și pot fi folosite numai pentru instrucțiuni. Ambele proceduri și funcții constau din două părți distincte, o parte declarativă între cuvintele cheie is și begin , care poate conține doar declarații (cum ar fi variabile, tipuri sau alte rutine imbricate) și o parte care conține doar instrucțiuni, între cuvintele cheie begin și end . Este posibil să inserați declarații în afara părții declarative folosind un bloc declare , iar declarațiile aferente sunt vizibile numai în interiorul acesteia. O rutină poate fi o unitate de compilare autonomă sau poate fi declarată și implementată într-un pachet sau în partea declarativă a unei alte rutine.

Parametrii unei rutine sunt specificați între paranteze rotunde, iar Ada acceptă trecerea parametrilor atât prin valoare, cât și prin referință, în trei moduri diferite: in , out și in out . Parametrii au trecut în modul in pot fi citite numai de rutină, cei din modul de out poate fi scris numai, cei în modul in out poate fi citit și scris. Modul unui parametru este specificat după i : și înainte de tip și, dacă este omis, se presupune in este implicit. Este posibil să se definească o valoare implicită pentru parametri, care în acest fel devin opționale și pot fi omise în apelurile la procedură: în acest caz se utilizează valoarea implicită. Pentru parametrii și valorile returnate ale tipului de acces este posibilă specificarea excluderii nule, ceea ce determină o excepție de execuție dacă parametrul (sau valoarea returnată) sunt null . Rutinele sunt invocate urmărindu-și numele cu o pereche de paranteze rotunde care conțin parametrii, dar dacă o rutină este declarată sau apelată fără a trece niciun parametru, parantezele rotunde pot fi omise. Rutinele definite pe tipurile etichetate pot fi, de asemenea, apelate cu notare punct în raport cu parametrul tip etichetat. [36]

 - exemplu de procedură cu parametru opțional
procedura Increment ( X : In out Integer ; Increment : Integer : = 1 ) este
începe
  X : = X + Increment ;
sfârșit Increment ;

- exemplu de apel într-o altă bucată de cod
procedurile P este
  N : întreg : = 0 ;
începe
  Increment ( N );
  - acum N este 1
  Creșterea ( N , 3 );
  - acum N este 4
capătul P ;

Operatorii sunt, de asemenea, funcții, definite prin indicarea numelui operatorului între ghilimele duble, iar Ada acceptă supraîncărcarea acestora, precum și rutina de suprasarcină și suprascriere în general. [37]

 - exemplu de suprasarcină a operatorului + cu operanzi de tip T
- a cărei sumă este definită în funcția Sum_T
funcția "+" ( Stânga , Dreapta : T ) returnează T este
începe
  returnează Suma_T ( Stânga , Dreapta );
sfârșitul „+” ;

Operatorii sunt identificați prin următoarele cuvinte cheie: [38]

 abs și mod not or rem xor
= / = << = >> =
+ - * / ** &

Pachet

Modularitatea codului la un nivel mai abstract decât rutinele se realizează prin intermediul pachetelor. Un pachet este împărțit în două părți distincte, specificație și implementare, iar unele compilatoare (cum ar fi GNAT ) necesită să introduceți o singură unitate de compilare pe fișier, împărțind de asemenea specificațiile și implementarea în două fișiere separate (respectiv .ads și .adb ). Specificația reprezintă interfața pachetului accesibilă extern și poate fi compilată independent de implementare, facilitând testarea timpurie. Implementarea conține codul propriu-zis al rutinelor definite în interfață, plus orice alte rutine neaccesibile din afara pachetului. Pachetele pot fi compilate separat și, dacă o implementare a pachetului se modifică fără a-și modifica specificațiile, orice alte pachete dependente de acesta nu trebuie recompilate. [39]

Declarațiile rutinelor conținute trebuie inserate în specificația pachetului (fișierul .ads ), în timp ce implementarea este plasată în corp (fișierul .adb ). Specificația și implementarea trebuie să aibă o conformitate completă a tipului , adică numele rutinei, orice tip de returnare, număr, tip, mod, ordine, valoare implicită și numele parametrilor trebuie să fie identice în specificație și în implementare, cu excepția cazului în care unele diferențe nesubstanțiale: un literal numeric poate fi înlocuit cu un diferit formal, dar cu aceeași valoare, un identificator poate fi adăugat ca prefix în notația punct , o indicație explicită a modului in poate fi omis, un set de parametri ai același subtip poate fi indicat separat. Toate rutinele declarate în caietul de sarcini trebuie neapărat să fie implementate în corp, cu câteva excepții: proceduri nule (a căror declarație se termină cu is null și este echivalentă cu o procedură care conține doar o declarație nulă), rutine abstracte (care nu au nicio implementare ) și funcții constând dintr-o expresie condițională (care este inserată direct în caietul de sarcini). [40]

 - specific
pachetul P este
  - rutine implementate în organism
  Proceduri de sumă ( A , B : în întreg ; S : în întreg );
  funcția Sum ( A , B : Întreg : = 0 ) returnează Întreg ;

  - rutine neimplementate în organism
  procedura Foo ( A : Integer ) este nulă ; - procedura nulă
  funcția Baz este abstractă ; - rutină abstractă
  function Bar ( A : Integer ) return Boolean is - functie constand dintr-o expresie conditionala
    ( dacă A > 0 atunci adevărat altceva fals );
capătul P ;

- implementare
corpul pachetului P este
  procedura Suma ( A , B : în întreg ; S : în întreg ) este
  începe
    - începutul instrucțiunilor
    S : = A + B ;
  sfarsit Suma ;

  funcție Sum ( A , B : Întreg : = 0 ) return Întreg este
    - începutul părții declarative
    C : întreg ;
  începe
    - începutul instrucțiunilor
    C : = A + B ;
    retur C ;
  sfarsit Suma ;
capătul P ;

Puteți crea o ierarhie de pachete în două moduri: cu cuibărirea, definirea unui pachet în altul și cu rudenia, definirea unui pachet ca fiind un copil al celuilalt (separarea părintelui și a copilului cu notare punct ). Pachetele pentru copii pot fi, de asemenea, definite ca private și, prin urmare, sunt total inaccesibile în afara pachetului părinte. [41]

 - pachet părinte
pachet Tatăl este
    - pachet imbricat
    pachetul Îmbricat este
      ...
    end Cuibărit ;
sfârșește Tată ;

- pachet pentru copii
pachet Tată.Fiu este
   ...
end Padre.Figlio ;

-- package figlio privato
private package Padre.Figlio_Privato is
  ...
end Padre.Figlio_Privato ;
I package forniscono un meccanismo di incapsulamento per tipi, variabili e subroutine, permettendo di definire una parte privata che non è accessibile al di fuori del package stesso, dei suoi package annidati e dei suoi figli. È anche possibile definire dei tipi con implementazione privata, che sono visibili utilizzabili al di fuori del package, ma la cui implementazione effettiva non è accessibile al di fuori dello stesso.

Tali tipi avranno perciò una vista parziale nella parte pubblica, e una vista completa nella parte privata del package. I package figli condividono la parte privata del padre ma non dei fratelli, tuttavia è possibile importare un package fratello in modo da poterne vedere anche la parte privata usando la clausola private with invece della solita with . [42]

 package P is
  type T is private ; -- T è visibile fuori da P, ma non la sua implementazione

private
  -- S non può essere visto né usato fuori dal package P
  type S is
    record
      ...
    end record ;

  -- completa la definizione di T
  type T is
    record
      -- i campi di T non sono visibili né utilizzabili fuori dal package P
      I : Integer ;
    end record ;
end P ;

Generics

In Ada le routine oi package possono avere dei parametri risolti staticamente a tempo di compilazione, specificati facendo precedere l'unità dalla keyword generic e specificati tra parentesi tonde quando si istanzia l'unità generica. I generics sono un meccanismo statico che consente un riuso del codice, permettendo di istanziare la stessa unità con parametri di tipo differenti, ma a differenza di altri linguaggi i parametri generici possono essere non solo tipi, ma anche valori o routine. Nel caso di package generici, non può essere usata su essi la clausola use (può essere usata solo sulle loro istanze concrete) ed ogni eventuale loro package figlio deve necessariamente essere generico. [43]

Ad esempio, una procedura per scambiare due elementi può essere parametrizzata rispetto al tipo degli stessi

 generic
  type T is private ;
procedure Scambia ( A , B : in out T );

procedure Scambia ( A , B : in out T ) is
  Temp : T ;
begin
  Temp := A ;
  A := B ;
  B := Temp ;
end Scambia ;

ed essere istanziata per l'uso su tipi diversi: [44]

 procedure Scambia_Interi is new Scambia ( Integer );
procedure Scambia_Float is new Scambia ( Float );

Analogamente un package può implementare uno stack generico

 generic
  type T is private ;
  Capacità : Positive ;
package Stack is
  procedure Push ( X : T );
  function Pop return T ;
private
  A : array ( 1 .. Capacità ) of T ;
  Posizione : Integer range 0 .. Capacità ;
end Stack ;

che può essere istanziato per contenere oggetti di differente tipo e scegliendo di volta in volta la capacità massima: [45]

 package Int_Stack is new Stack ( Integer , 100 ); -- stack con una capacità di 100 valori interi
package Float_Stack is new Stack ( Float , 50 ); -- stack con una capacità di 50 valori float

Parametri di tipo

Nel caso più semplice, un parametro di tipo generico è istanziabile e ha solo la definizione di assegnamento e uguaglianza. Possono essere specificati diversi attributi del parametro di tipo, che ne modificano l'interfaccia: può essere limitato, può avere dei discriminanti (anche non specificati, indicati con il box <> , in tal caso il tipo non è istanziabile), può essere tagged, astratto, può essere discendente di un tipo preesistente o può essere un'interfaccia con una o più interfacce progenitrici. Alcuni esempi di parametri di tipo generici: [46]

 generic
  type A is private ; -- tipo privato
  type B is limited ; -- tipo limitato
  type C ( X : Integer ); -- tipo con discriminante X
  type D (<>); -- tipo con discriminante sconosciuto
  type E is tagged ; -- tipo tagged
  type F is new T ; -- tipo derivato dal tipo non tagged T
  type G is new T with private ; -- tipo derivato dal tipo tagged T
  type H is interface and I and J ; -- tipo interfaccia con due progenitrici
package Generic_Package is
  ...
end Generic_Package ;

Per i tipi enumerativi e numerici esistono anche delle indicazioni particolari, e per ognuno di essi sono disponibili le operazioni predefinite per tale tipo. [47]

 generic
  type A is (<>); -- tipo enumerativo
  type B is range <>; -- tipo numerico con segno
  type C is mod <>; -- tipo numerico modulare
  type D is digits <>; -- tipo in virgola mobile
  type E is delta <>; -- tipo in virgola fissa
  type F is delta <> digits <>; -- tipo decimale

Parametri routine

Poiché per un tipo generico si assume l'esistenza delle sole funzioni predefinite per quella categoria di tipi, può essere necessario passare come parametro anche una o più routine. Ad esempio, implementando un albero binario di ricerca che possa contenere chiavi di un tipo qualsiasi, per effettuare le operazioni di inserimento o ricerca è necessario poter comparare le chiavi, ma l'operatore di confronto < non è predefinito per ogni tipo. Si può quindi ovviare al problema passando tale operatore come parametro generico. I parametri generici per le routine devono essere quindi passati esplicitamente quando si istanzia il package, ma possono essere omessi se nella dichiarazione del parametro generico si specifica is <> . In questo caso la routine viene scelta automaticamente quando si istanzia il package, a patto che sia definita e visibile, sia unica e abbia completa type conformance con la dichiarazione del parametro generico. [48]

 -- package generico
generic
  type T is private ;
  with function "<" ( Sinistra , Destra : T ) return Boolean is <>;
package Albero_Binario is
  ...
end Albero_Binario ;

-- package che usa l'Albero_Binario
package P is
  type Data is
    record
      ...
    end record ;
  function Confronta_Data ( Sinistra , Destra : Data ) return Boolean ;

  -- istanzia il package generico sul tipo Data
  package Albero_Data is new Albero_Binario ( Data , Confronta_Data );

  -- l'operatore può anche essere omesso, perché esiste ed è visibile per il tipo Float
  package Albero_Float is new Albero_Binario ( Float );
end P ;

Memoria dinamica

La memoria dinamica (chiamata storage pool ) è gestita ad alto livello ed è typesafe . Il linguaggio non fornisce puntatori flessibili come quelli del C (che sono una tra le principali fonti di bug, errori e vulnerabilità), ma utilizza dei riferimenti (detti access type ) che conservano informazioni sull'accessibilità degli oggetti ai quali fanno riferimento e seguono precise regole di accessibilità, prevenendo il problema dei dangling pointer . La versatilità del linguaggio per applicazioni a basso livello è comunque garantita, grazie all'attributo 'Address e al package System , che permettono di manipolare indirizzi di memoria raw. L'interfaccia Interfaces.C.Pointers fornisce inoltre dei puntatori in stile C, utili quando si interfaccia un'applicazione Ada con una in C.

La semantica del linguaggio permette la garbage collection , la cui presenza è però legata all'implementazione: solitamente è assente nei compilatori per le architetture native (in quanto può influire in maniera imprevedibile sul timing, e questo è un effetto deleterio nei sistemi real-time ) [49] ma è talvolta presente per i compilatori che hanno come architettura target la JVM . La deallocazione può essere effettuata manualmente, istanziando l'unità generica Ada.Unchecked_Deallocation , che deve essere usata con attenzione per evitare di deallocare oggetti nello stack o creare dangling pointer. [50] Per aumentare la sicurezza si può applicare un pattern di smart pointer , creando oggetti che contano e gestiscono autonomamente i riferimenti alle risorse, in modo che il programmatore del client non debba deallocare niente in maniera esplicita. [51]

Programmazione orientata agli oggetti

A differenza di molti linguaggiorientati agli oggetti , Ada non ha un costrutto per le classi analogo a C++ o Java. Tra i principali aspetti della programmazione OOP vi sono la possibilità di distinguere il tipo di un oggetto a runtime, di definire un tipo a partire da un altro e di permettere a tali tipi di ereditare le operazioni primitive del tipo da cui deriva. [52] In Ada la differenza tra variabili che sono "oggetti" e che non lo sono consiste nel fatto che le prime conservano a runtime le informazioni sul proprio tipo, consentendo il polimorfismo e il dynamic dispatch .

I tipi che riportano tale informazione sono detti tagged (che significa "etichettati"), e viene specificato nella dichiarazione del tipo con l'omonima keyword. [53]

Ereditarietà

L' ereditarietà si ha tramite estensione, che rende possibile aggiungere nuovi campi, mantenendo anche quelli ereditati. È possibile convertire un oggetto (record tagged) di un sottotipo in un tipo antenato, mentre non è possibile fare il contrario, ma è possibile assegnare un oggetto di tipo antenato ad un tipo discendente usando un aggregato che completi i campi mancanti ( extension aggregate ). L'estensione S di un tipo tagged T tramite la keyword new eredita anche le operazioni primitive per T , ovvero quelle subroutine dichiarate nello stesso package in cui è dichiarato T e che abbiano un parametro o un risultato di tipo T . [53] I tipi derivati hanno quindi un'interfaccia che è sempre un sovrainsieme di quella del tipo da cui derivano, e l'implementazione effettiva delle operazioni ereditate può essere modificata tramite override (l'indicazione esplicita di override nella dichiarazione dell'operazione tramite la keyword overriding , o viceversa di non override con not overriding , è facoltativa ma costituisce un utile controllo statico a tempo di compilazione). [54] Le funzioni di un tipo tagged possono essere richiamate con la dot notation sulla variabile dell'oggetto. L'incapsulamento dello stato interno degli oggetti non è diverso da quello per i tipi non tagged, ed è ottenuto usando il meccanismo di incapsulamento dei package . L'estensione di un tipo può avvenire anche privatamente, per cui i campi aggiunti nell'estensione non sono visibili all'esterno del package. [55]

 package Persone is
  type Persona is tagged -- tipo tagged
    record
      Nome : String ;
      Cognome : String ;
      Età : Natural ;
    end record ;
  function Salario ( P : Persona ) return Float ; -- operazione primitiva, restituisce zero come default
  
  type Lavoratore is new Persona with -- tipo derivato aggiungendo nuovi campi
    record
      Mansione : Job ; -- un qualche tipo enumerativo
      Anzianità : Natural ;
    end record ;
  overriding
  function Salario ( L : Lavoratore ) return Float ; -- override di un'operazione primitiva

  type Studente is new Persona with
    record
      Istituto : School ; -- tipo enumerativo
      Corso : Year ; -- altro tipo enumerativo
    end record ;
  -- Studente eredita la funzione Salario di Persona, che restituisce zero

  -- estensione privata
  type Dottorando is new Studente with private ;

  -- dichiarazione di un oggetto Studente
  S : Studente := ( "John" , "Doe" , 20 , "Trinity College" , III );

  -- conversione da un sottotipo ad un tipo antenato
  P : Persona := Persona ( S );

  -- assegnamento ad un sottotipo con un extension aggregate
  T : Studente := ( P with Istituto => "MIT" , Corso => IV );

private
  type Dottorando is new Studente with
    record
      -- i campi aggiunti non sono visibili fuori dal package
      Dipartimento : Department ;
    end record ;
end Persone ;

Dynamic dispatch

Con il termine "classe" in Ada si indica un insieme di tipi ( class wide ), costituito da un tipo tagged e da tutti i tipi da esso derivati direttamente o indirettamente. È possibile definire operazioni che hanno parametri o risultato di un tipo class wide usando l'attributo 'Class , ad esempio per un tipo T il suo tipo class wide viene indicato con T'Class . La differenza tra un'operazione con un parametro di tipo T e uno di tipo T'Class è che nel primo caso la scelta della routine da eseguire è determinata staticamente a tempo di compilazione, nel secondo caso è determinata dinamicamente a runtime (dynamic dispatch). [56] Se nell'esempio precedente si aggiunge al tipo Persona la seguente operazione primitiva

 function Reddito_Annuo ( P : Persona ) return Float is
  return 12.0 * P . Salario ;
end Reddito_Annuo ;

si ha che la funzione restituirà sempre zero per tutti gli oggetti, anche dei tipi come Lavoratore che avessero salario non nullo, perché al suo interno viene sempre richiamata staticamente la funzione Salario definita per il tipo Persona , che restituisce zero. Se invece l'operazione è definita come

 function Reddito_Annuo ( P : Persona ' Class ) return Float is
  return 12.0 * P . Salario ;
end Reddito_Annuo ;

il dispatch della funzione Salario avviene dinamicamente e il risultato restituito è quello corretto anche per gli oggetti di tipo Lavoratore . [57]

Tipi astratti e interfacce

Se un tipo tagged viene dichiarato astratto, tramite la keyword abstract , non è possibile dichiarare variabili di quel tipo, ma solo usarlo come base da cui derivare altri tipi. I tipi astratti possono avere componenti e routine concrete, ma anche routine a loro volta definite come astratte, che non sono provviste di implementazione. Quando si deriva un tipo concreto da un tipo astratto, tutte le sue routine astratte devono necessariamente essere oggetto di override. Un'interfaccia è un tipo dichiarato con la keyword interface , ed è analogo ad un tipo astratto ma ha maggiori restrizioni, in quanto non può avere componenti né routine concrete, salvo procedure nulle o routine con parametri class wide .

Ada ha un meccanismo di ereditarietà singola per le implementazioni e multipla per le interfacce, simile al Java, per cui è possibile definire tipi che estendono al massimo un tipo concreto o astratto, ma allo stesso tempo possono implementare un numero arbitrario di interfacce. Questo previene possibili conflitti o ambiguità derivanti dall'ereditarietà multipla completa, ma conserva comunque la flessibilità consentendo ad un tipo di poter avere più interfacce di routine. [58] Le interfacce possono essere usate anche con estensioni private, ma in quel caso la vista completa e quella parziale del tipo devono essere conformi rispetto alle interfacce implementate, per cui non è possibile aggiungere o togliere interfacce nel completamento privato della dichiarazione. [59]

 package P is
  -- tipo astratto: può avere campi, procedure concrete e astratte
  type S is abstract tagged
    record
      ...
    end record ;
  procedure Foo ( X : S );
  procedure Baz ( X : S ) is abstract ;

  -- interfaccia: non può avere campi né operazioni concrete (che non
  -- siano nulle o con parametro class wide)
  type T is interface ;
  procedure Foo ( X : T ) is abstract ;
  procedure Baz ( X : T ) is null ;
  procedure Bar ( X : T ' Class );
end P ;

Tipi controllati

In Ada non esiste il concetto di costruttore , ma è possibile sostituirne le caratteristiche funzionali usando un tipo controllato. Un tipo controllato è un tipo che estende Ada.Finalization.Controlled (oppure Ada.Finalization.Limited_Controlled per i tipi controllati e limitati), per il quale è possibile eseguire l'override di tre procedure (due nel caso dei tipi limitati, dove manca la procedura Adjust ):

 with Ada.Finalization ;
package P is
  type T is new Ada . Finalization . Controlled with
    record
      ...
    end record ;

  overriding procedure Initialize ( This : in out T );
  overriding procedure Adjust ( This : in out T );
  overriding procedure Finalize ( This : in out T );
end P ;

La procedura Initialize viene eseguita sull'oggetto subito dopo la creazione e può svolgere le funzionalità di inizializzazione tipicamente delegate ad un costruttore, [60] la procedura Adjust viene eseguita subito dopo un'assegnazione (per cui non è disponibile per i tipi Limited_Controlled ) e può fungere da costruttore di copia, mentre la procedura Finalize viene eseguita immediatamente prima della deallocazione di un oggetto, e funge da distruttore . [61]

Per ragioni storiche, i tipi Ada.Finalization.Controlled e Ada.Finalization.Limited_Controlled non sono interfacce (aggiunte solo in Ada 2005) ma tipi astratti, per cui non è possibile definire un tipo che sia controllato e che contemporaneamente erediti l'implementazione di un tipo non controllato. [59]

Note

  1. ^ John Barnes , The Ravenscar profile , su adaic.org .
  2. ^ S. Tucker Taft e Florence Olsen, Ada helps churn out less-buggy code , su gcn.com , Government Computer News, 30 giugno 1999, pp. 2–3. URL consultato il 14 settembre 2010 ( archiviato il 31 agosto 2015) .
  3. ^ Michael Feldman, Who's using Ada? , su seas.gwu.edu , SIGAda Education Working Group ( archiviato il 31 agosto 2015) .
  4. ^ Pulling strings 220 miles above Earth - The ISS software serves as the orbiting lab's central nervous system ( PDF ), Boeing (archiviato dall' url originale il 23 aprile 2015) .
  5. ^ Barnes (2014) , p. 380 .
  6. ^ Gary Dismukes, Gem #63: The Effect of Pragma Suppress , su adacore.com ( archiviato il 28 luglio 2015) .
  7. ^ Il programma, salvato nel file hello.adb , può essere compilato usando il compilatore GNAT con il comando gnatmake hello.adb
  8. ^ Fanno eccezione i letterali di tipo carattere o stringa.
  9. ^ Barnes (2014) , p. 67 .
  10. ^ Barnes (2014) , p. 851 .
  11. ^ Barnes (2014) , p. 68 .
  12. ^ Barnes (2014) , pp. 73-74 .
  13. ^ Barnes (2014) , p. 75 .
  14. ^ La label come ultima riga del blocco è valida solo a partire dallo standard Ada 2012, mentre le versioni precedenti richiedevano che il goto fosse seguito da un'istruzione (quindi bisognava aggiungere un'istruzione nulla al termine del blocco, dopo la label).
  15. ^ Barnes (2014) , p. 114 .
  16. ^ Peter Van der Linden, Expert C Programming: Deep C Secrets , prentice Hall Professional, 1994, pp. 36-38, ISBN 978-0-13-177429-2 .
  17. ^ Barnes (2014) , p. 11 .
  18. ^ Barnes (2014) , p. 211 .
  19. ^ Barnes (2014) , p. 83 .
  20. ^ Barnes (2014) , p. 87 .
  21. ^ Barnes (2014) , p. 18 .
  22. ^ Taft et al. , pp. 356-359 .
  23. ^ Taft et al. , pp. 319-322 .
  24. ^ Barnes (2014) , pp. 77-79 .
  25. ^ Barnes (2014) , pp. 87-89 .
  26. ^ Barnes (2014) , p. 117 .
  27. ^ Barnes (2014) , p. 112 .
  28. ^ Barnes (2014) , p. 137 .
  29. ^ Barnes (2014) , pp. 118, 135-136 .
  30. ^ Barnes (2014) , p. 128 .
  31. ^ Barnes (2014) , p. 138 .
  32. ^ Barnes (2014) , pp. 143-146 .
  33. ^ Barnes (2014) , pp. 439 ss.
  34. ^ Barnes (2014) , p. 251 .
  35. ^ In questa voce si usano distintamente i tre termini, con significato differente: "funzione" per indicare un sottoprogramma che ha un valore di ritorno, "procedura" per indicare un sottoprogramma che non ha un valore di ritorno, "routine" per indicare un generico sottoprogramma (procedura o funzione).
  36. ^ Barnes (2014) , pp. 180 ss.
  37. ^ Barnes (2014) , pp. 181-182, 185 .
  38. ^ Barnes (2014) , p. 169 .
  39. ^ Barnes (2014) , pp. 265-266 .
  40. ^ Barnes (2014) , p. 183 .
  41. ^ Barnes (2014) , pp. 272-273 .
  42. ^ Barnes (2014) , p. 277 .
  43. ^ Barnes (2014) , pp. 469 ss.
  44. ^ Barnes (2014) , p. 470 .
  45. ^ Barnes (2014) , p. 471 .
  46. ^ Barnes (2014) , p. 475 .
  47. ^ Barnes (2014) , p. 477 .
  48. ^ Barnes (2014) , pp. 485-491 .
  49. ^ Bruce Powel Douglass,Doing Hard Time: Developing Real-time Systems with UML, Objects, Frameworks, and Patterns , Addison-Wesley, 1999, p. 91 , ISBN 978-0-201-49837-0 .
  50. ^ Barnes (2014) , p. 787 .
  51. ^ CKW Grein, Preventing Deallocation for Reference-counted Types , su adacore.com , AdaCore ( archiviato il 31 luglio 2015) .
  52. ^ Barnes (2014) , p. 30 .
  53. ^ a b Barnes (2014) , p. 31 .
  54. ^ Barnes (2014) , p. 306 .
  55. ^ Barnes (2014) , p. 334 .
  56. ^ Barnes (2014) , pp. 34-35 .
  57. ^ Barnes (2014) , p. 35 .
  58. ^ Barnes (2014) , pp. 347-348 .
  59. ^ a b Barnes (2014) , p. 350 .
  60. ^ Tecnicamente non si tratta di un costruttore, che viene eseguito durante la creazione dell'oggetto e dopo l'esecuzione dei costruttori di eventuali superclassi.
  61. ^ Barnes (2014) , pp. 342-346 .

Bibliografia

Inglese

Altri progetti

Collegamenti esterni

Controllo di autorità LCCN ( EN ) sh85000774 · GND ( DE ) 4000430-2 · BNE ( ES ) XX531014 (data)
Informatica Portale Informatica : accedi alle voci di Wikipedia che trattano di informatica