Toate tipurile definite de utilizator includ un constructor. Chiar dacă nu scrieți unul explicit, compilatorul va scrie unul pentru dvs. Pentru majoritatea claselor și structurilor văzute până acum, lăsăm compilatorul să genereze constructorii impliciți pentru noi.

constructorul implicit

Constructorul implicit poate fi suprasolicitat ca orice altă funcție. Când vă proiectați tipurile, puteți crea constructori cu unul, două sau mai multe argumente, după caz.

Compilatorul o va face nu generăm un constructor implicit dacă scriem altele care nu sunt implicite. Compilatorul poate crea în continuare alte operații implicite.

Deci, în timp ce acest lucru este în regulă:

Putem inițializa punctul nostru numai folosind constructorul implicit.

Dacă dorim să inițializăm membrii clasei la momentul construcției, atunci trebuie să adăugăm constructori personalizați.

Acum putem folosi constructorul nostru cu 2 argumente, dar acum vechea noastră invocare implicită este ruptă. Veți vedea și veți greși astfel:

Acest lucru este fixat de:

Scrierea propriei noastre implementări implicite

Spunându-i compilatorului să o scrie

Spunându-i compilatorului să-l șteargă

În acest caz, încercarea de a utiliza constructorul implicit este încă o eroare de compilare, dar eroarea este mai explicită: nu o puteți folosi deoarece a fost ștearsă.

Dacă scrieți un constructor non-implicit, atunci ar trebui să scrieți întotdeauna propriul constructor implicit sau să instruiți în mod explicit compilatorul să îl facă pentru dvs. sau să îl ștergeți.

În general, aveți grijă să ștergeți constructorul implicit. Ștergeți-l numai atunci când sunteți sigur că vor face obiectele clasei nu trebuie să fie construit implicit.

7.1.1. Inițializare a sintaxei

Unii programatori care vin în C ++ din alte limbaje OO simt uneori că ar trebui să inițializeze obiecte de genul acesta:

Chiar dacă tot semestrul, scriai:

Când vine vorba de tipurile definite de utilizator, uneori se simte „incomplet” dacă nu includeți (). De obicei, aceste paranteze creează mai multe probleme decât rezolvă. Acest lucru se datorează unei ambiguități inerente în limbajul C ++. Deși ni se pare evident punctul de declarație p (); este un apel către constructorul implicit și rezultatele ar trebui să fie o nouă variabilă p, compilatorul o interpretează diferit.

Regula de bază este:

Aceasta înseamnă că în codul de mai sus, compilatorul caută în schimb:

o funcție numită p

care nu ia argumente

și returnează un obiect de tip punct

Deoarece în acest caz, nu există o astfel de funcție, aceasta returnează o eroare. Unii compilatori, cum ar fi clang, vor încerca să vă spună:

C ++ rezolvă această ambiguitate în C ++ 11 utilizând sintaxa uniformă a inițializatorului. Puteți utiliza paranteze: <> în loc de paranteze pentru a inițializa obiecte. Parantezele sunt o extensie a sintaxei listei de inițializatoare pentru containere și pot fi utilizate chiar și pentru obiectele construite implicit.

În timp ce cele de mai sus funcționează de fiecare dată, este preferată omiterea parantezelor în întregime atunci când nu este necesară:

Sintaxa inițializatorului funcționează și în cadrul constructorilor.

Reamintim că, pentru containere, există o diferență între vectorul (5) și vectorul. Care este diferența?

Prima versiune creează un vector de dimensiunea 5 fără valori inițializate.

A doua versiune creează un vector de dimensiunea 1 cu o singură valoare egală cu 5.

7.1.2. Constructori supraîncărcați¶

Aceleași linii directoare care se aplică la scrierea funcțiilor bune se aplică scrierii unor constructori suprasolicitați. O clasă bună este construită în jurul funcțiilor bune. La fel ca în cazul funcțiilor obișnuite, evitați confundarea listelor de parametri. Luați în considerare următoarele:

Se pare că cei trei parametri reprezintă anul, luna și ziua, dar fără a citi codul, nu există nicio modalitate de a ști ce ordine.

Chiar dacă citim codul și învățăm comanda, este totuși probabil să uităm comanda și să transpunem o lună și o zi la un moment dat.

În loc să ne resemnăm să sperăm să ne amintim sau să avem probleme de depanare în timpul rulării, simpla definire a tipurilor adecvate îmbunătățește claritatea și utilitatea:

Această versiune este mai ușor de reținut pentru programatori și orice erori sunt erori de compilare în loc de erori de runtime.

7.1.3. Constructori telescopici¶

Clasa inițială de dată a suferit de o problemă comună de proiectare: prea mulți parametri de același tip. O problemă strâns legată este cum să oferiți flexibilitate atunci când construiți obiecte noi. O soluție comună este de a oferi constructorilor un număr diferit de argumente:

Dar despre posibilitatea de a specifica luna și ziua? Câți constructori diferiți ar trebui să li se permită? Numărul permutărilor devine imposibil de menținut chiar și pentru un număr relativ mic de parametri.

Acesta se numește constructor telescopic și este considerat, în general, un anti-model. Adică, există soluții mai bune la această problemă.

Cea mai ușoară soluție în C ++ este utilizarea valorilor implicite pentru parametrii funcției. Acest lucru funcționează cel mai bine atunci când valorile implicite sunt diferite tipuri și nu este necesar să se permită fiecare combinație posibilă de parametri.

Această soluție este încă limitată de faptul că valorile implicite sunt încă evaluate de la stânga la dreapta. O declarație de dată a formularului

nu va crea o dată pentru a 15-a zi a lunii și a anului curent. În plus, soluția nu funcționează bine atunci când toți (sau majoritatea) parametrilor sunt de același tip. Luați în considerare acest exemplu:

Ordinea corectă este caloriile, grăsimile, carbohidrații sau grăsimile, caloriile, carbohidrații sau altceva? Chiar dacă acordăm acestor parametri nume semnificative, nu există nicio aplicare în timp de rulare. Este ușor să faceți o greșeală atunci când prea mulți parametri sunt de același tip.

Când se confruntă cu mulți parametri opționali, un constructor este o alternativă eficientă. Idei de bază:

Utilizați parametrii constructor pentru a accepta parametrii obligatorii.

Utilizați o clasă de ajutor (Builder) pentru a inițializa parametrii opționali implicit.

O funcție Builder: build () creează un obiect NutritionFacts de la un constructor.

Constructorul face clasa pe care o ajută un prieten.

Aceasta este utilizată doar pentru a evita crearea funcțiilor de accesare a constructorului.

Un constructor de conversie este utilizat pentru a copia starea constructorului în clasa de închidere.

Când sunt complete, clasele pot fi utilizate astfel:

Deși nu este cea mai idiomatică soluție C ++, este ceva ce putem crea și folosi doar cu cunoștințele de clase pe care le avem până acum. Vom revizita modelul constructorului mai târziu după ce acoperim moștenirea.

Mai multe de explorat

Model de proiectare a constructorului:

Java eficient, de Joshua Bloch. Elementul 2: Luați în considerare un constructor atunci când vă confruntați cu mulți parametri de constructor