În programarea orientată pe obiecte , un constructor de clasă (din engleza constructor ) este un bloc special de instrucțiuni numit atunci când este creat un obiect.
Una dintre caracteristicile cheie ale OOP este încapsularea : câmpurile interne ale clasei nu sunt direct accesibile, iar utilizatorul poate lucra doar cu obiectul ca întreg, prin publicmetode publice ( ). În mod ideal, fiecare metodă ar trebui proiectată astfel încât un obiect care se află într-o stare „validă” (adică atunci când invariantul clasei este îndeplinită ) să fie, de asemenea, într-o stare validă atunci când metoda este invocată. Și prima sarcină a constructorului este să transfere câmpurile obiectului într-o astfel de stare.
A doua sarcină este de a simplifica utilizarea obiectului. Un obiect nu este un „ lucru în sine ”, de multe ori trebuie să necesite anumite informații de la alte obiecte: de exemplu, un obiect File, atunci când este creat, trebuie să primească un nume de fișier. Acest lucru se poate face și prin metoda:
fisier fisier ; dosar . deschide ( "in.txt" , Fișier :: omRead );Dar este mai convenabil să deschideți fișierul în constructor: [1]
Fișier ( "in.txt" , Fișier :: omRead ) ;O varietate de limbaje de programare prezintă mai multe varietăți de constructori:
Constructorii care preiau unul sau mai multe argumente sunt numiți parametrizați. De exemplu:
exemplu de clasă { int x , y ; public : exemplu (); Exemplu ( int a , int b ); // constructor parametrizat }; Exemplu :: Exemplu () { } Exemplu :: Exemplu ( int a , int b ) { x = a ; y = b ; }Un constructor parametrizat poate fi apelat explicit sau implicit, de exemplu:
Exemplul e = Exemplu ( 0 , 50 ); // apel explicit Exemplul e ( 0 , 50 ); // apel implicitUn constructor fără argumente necesare. Folosit la crearea matricelor de obiecte, apelate pentru a crea fiecare instanță. În absența unui constructor implicit explicit, codul acestuia este generat de compilator (care, desigur, nu este reflectat în textul sursă).
Un constructor al cărui argument este o referință la un obiect din aceeași clasă. Folosit în C++ pentru a trece obiecte la funcții după valoare .
Constructorul de copiere este necesar mai ales atunci când un obiect are pointeri către obiecte alocate pe heap . Dacă programatorul nu creează un constructor de copiere, atunci compilatorul va crea un constructor de copiere implicit care copiază pointerii așa cum sunt , adică nu are loc nicio copiere reală a datelor și cele două obiecte se referă la aceleași date de pe heap. În consecință, o încercare de a schimba „copie” va deteriora originalul, iar apelarea destructorului pentru unul dintre aceste obiecte, cu utilizarea ulterioară a celuilalt, va duce la accesul la o zonă de memorie care nu mai aparține programului.
Argumentul trebuie transmis prin referință , nu prin valoare . Aceasta rezultă dintr-o coliziune: atunci când treceți un obiect după valoare (în special, pentru a apela un constructor), este necesar să copiați obiectul. Dar pentru a copia un obiect, trebuie să apelați constructorul de copiere.
Un constructor care ia un singur argument. Specifică conversia tipului argumentului său în tipul constructorului. Această conversie de tip se aplică implicit numai dacă este unică.
O conversie de tip definită de utilizator poate lua una din două forme: - de la o clasă de tip C la orice tip T, pentru care C trebuie să aibă un C::operator T() - de la orice tip T la o clasă de tip C, pentru care C trebuie să aibă C::C(T) (sau C::C(T&), sau C::C(T&&))
Dacă ambele cazuri sunt permise într-o expresie, apare o ambiguitate și o eroare de compilare.
Dacă un constructor (sau operatorul T()) este marcat cu cuvântul cheie explicit, atunci o astfel de conversie de tip este aplicată numai dacă există o operație de turnare explicită de forma (T)C sau static_cast<T>C. Dacă nu există un cuvânt explicit, atunci compilatorul poate insera o astfel de conversie chiar și implicit, de exemplu, când apelează funcția f(T arg) sub forma f(C).
C ++11 introduce un nou tip de referințe non-constante numite rvalue reference și notate ca T&&și un nou tip de constructor - mutați constructori . Constructorul de mutare ia ca intrare valoarea unei referințe non-constante la un obiect de clasă și este folosit pentru a transfera proprietatea asupra resurselor acelui obiect. Constructorii de mutare au fost inventați pentru a rezolva pierderea de eficiență asociată cu crearea de obiecte temporare.
Un constructor nu este virtual în sensul unei metode virtuale - pentru ca mecanismul metodelor virtuale să funcționeze, trebuie să rulați constructorul, care va configura automat tabelul de metode virtuale al acestui obiect.
„Constructori virtuali” se referă la un mecanism similar, dar diferit, găsit în unele limbi, cum ar fi Delphi , dar nu C++ și Java . Acest mecanism vă permite să creați un obiect din orice clasă necunoscută anterior în două condiții:
Așa-numitul tip de clasă ( metaclasă ) este introdus în limbaj. Acest tip poate lua ca valoare numele oricărei clase derivate din TVehicle.
tip CVehicle = clasa de vehicul TV ;Acest mecanism vă permite să creați obiecte din orice clasă necunoscută anterior derivată din TVehicle.
var cv : CVehicle ; v : vehicul TV ; cv := TAutomobile ; v := cv . a crea ;Observați că codul
cv := TMoped ; v := cv . a crea ;este incorectă - directiva a reintroducerupt lanțul de depășire a metodei virtuale și, de fapt, constructorul va fi apelat TMotorcycle.Create(ceea ce înseamnă că va fi creată o motocicletă, nu o mopedă!)
Vezi și Fabrică (model de design)
Numele constructorului trebuie să se potrivească cu numele clasei. Sunt permisi mai mulți constructori cu același nume, dar cu parametri diferiți .
Exemplu clasa ClassWithConstructor { public : /* Inițializați obiectul intern cu constructorul */ ClassWithConstructor ( parametru float ) : obiect ( parametru ) {} /* apel constructor AnotherClass(float); */ privat : obiect AnotherClass ; };În Python, un constructor este o metodă de clasă numită __init__. De asemenea, nu uitați că primul argument al oricărei metode trebuie să fie un pointer către contextul clasei self.
Exemplu clasa ClassWithConstructor : def __init__ ( self ): """Această metodă este constructor.""" treceLimbajul Ruby folosește o metodă specială pentru a seta un obiect la starea sa inițială consistentă initialize.
Exemplu class ClassWithConstructor def initialize print 'Această metodă este constructor.' sfârşitul sfârşituluiÎn Delphi , spre deosebire de C++ , constructorul este declarat cu cuvântul cheie constructor. Numele constructorului poate fi orice, dar este recomandat să denumim constructorul Create.
Exemplu TClassWithConstructor = constructor public de clasă Creare ; sfârşitul ;Câteva diferențe între constructori și alte metode Java :
În JavaScript , constructorul este o funcție obișnuită folosită ca operand al operatorului new. Cuvântul cheie este folosit pentru a se referi la obiectul creat this.
Cu toate acestea, specificația ECMAScript 6 a adăugat un înveliș sintactic prototip, care are proprietăți OOP precum moștenirea, precum și o listă mică de metode necesare, de exemplu: toString().
Exemplu function Exemplu ( initValue ) { this . myValue = initValue ; } exemplu . prototip . getMyValue = function () { return this . valoarea mea ; } //ES6 clasa clasa Exemplu { constructor () { console . jurnal ( 'constructor' ); } } // cod care ilustrează crearea unui obiect de către constructorul descris mai sus var exampleObject = new Example ( 120 );Constructorii din Visual Basic .NET folosesc o metodă obișnuită de declarare numită New.
Exemplu Clasa Foobar Private strData As String ' Constructor Public Sub New ( ByVal someParam As String ) strData = someParam End Sub End Class „un cod ” care ilustrează crearea unui obiect de către constructorul Dim foo As New Foobar ( ".NET" ) de mai susÎn Eiffel , rutinele care inițializează obiecte sunt numite proceduri de creație . Procedurile de creare sunt oarecum similare cu constructorilor și oarecum diferite. Au următoarele caracteristici:
Deși crearea unui obiect este subiectul unor subtilități [Nota 3] , crearea unui atribut cu o declarație de tip x: Texprimată ca instrucțiune de creare create x.makeconstă în următoarea secvență de pași:
Primul pasaj de mai jos definește clasa POINT. Procedura makeeste codificată după cuvântul cheie feature.
Cuvântul cheie createintroduce o listă de proceduri care pot fi utilizate pentru a inițializa instanțe ale clasei. În acest caz, lista conține default_create, o procedură cu o implementare goală moștenită de la clasă ANYși o procedură makecu o implementare în clasa însăși POINT.
clasa POINT create default_create , make caracteristică make ( a_x_value : REAL ; a_y_value : REAL ) do x := a_x_value y := a_y_value end x : REAL -- coordonata X y : REAL -- coordonata Y ...În al doilea pasaj, clasa care este clientul clasei POINTare declarații my_point_1de my_point_2tip POINT.
În codul subrutinei , acesta my_point_1este creat cu coordonate (0.0; 0.0). Deoarece nu este specificată nicio procedură de creare în instrucțiunea de creare, se folosește procedura default_createmoștenită de la clasă ANY. Aceeași linie ar putea fi rescrisă ca create my_point_1.default_create. Numai procedurile specificate ca proceduri create pot fi utilizate în instrucțiunile create (adică, instrucțiunile cu cuvântul cheie create).
Urmează instrucțiunea de creare pentru my_point_2, care stabilește valorile inițiale pentru coordonate my_point_2.
A treia instrucțiune face un apel de procedură normală makepentru a reinițializa instanța la care este atașată my_point_2cu valori diferite.
my_point_1 : POINT my_point_2 : POINT ... create my_point_1 create my_point_2 . face ( 3.0 , 4.0 ) punctul meu_2 . face ( 5.0 , 8.0 ) ...Trebuie remarcat faptul că nu există o metodă de construcție în ColdFusion . O metodă comună în rândul comunității de programare ColdFusion este de a apela metoda „ ” initca pseudo-constructor.
<cfcomponent displayname = "Brânză" > <!--- proprietăți ---> <cfset variabile . cheeseName = "" / > <!--- pseudo-constructor ---> <cffunction name = "init" returntype = "Cheese" > <cfargument name = "cheeseName" type = "string" required = "true" / > < variabile cfset . cheeseName = argumente . cheeseName / > <cfreturn this / > </cffunction> </cfcomponent>În PHP (începând cu versiunea 5), un constructor este o metodă __construct()care este apelată automat de un cuvânt cheie newdupă ce un obiect a fost creat. Utilizat de obicei pentru a efectua diverse inițializari automate, cum ar fi inițializarea proprietăților. Constructorii pot lua, de asemenea, argumente, caz în care, atunci când este specificată o expresie new, parametrii formali trebuie să fie trecuți constructorului în paranteze.
clasa Persoana { private $nume ; function __construct ( $nume ) { $acest -> nume = $nume ; } function getName () { return $this -> name ; } }Cu toate acestea, un constructor în PHP versiunea 4 (și anterioară) este o metodă de clasă cu același nume de clasă.
clasa Persoana { private $nume ; function Persoana ( $nume ) { $acest -> nume = $nume ; } function getName () { return $this -> name ; } }În Perl , constructorul trebuie să aplice funcția bless unei variabile (de obicei, o referință hash):
Exemplu pachet ; sub new { my $class = shift ; my $self = {}; return bless $self , $class ; } 1 ;Dar aceasta este opțiunea de bază minimă, există multe metode mai avansate, de la câmpuri de utilizare până la Moose.
Constructorii fac întotdeauna parte din implementarea claselor. O clasă (în programare) descrie specificațiile caracteristicilor de bază ale setului de obiecte care sunt membri ai clasei, nu caracteristicile individuale ale vreunuia dintre obiecte. Să ne uităm la o analogie simplă. Luați ca exemplu un set (sau clasă, pentru a folosi sensul său mai general) de elevi dintr-o anumită școală. Astfel avem:
elev de clasă { // descrierea clasei de elev // ... alt cod ... }Cu toate acestea, clasa Student este doar un șablon general (prototip) pentru studenții noștri. Pentru a-l folosi, programatorul creează fiecare elev ca obiect sau entitate ( implementare ) a clasei. Acest obiect este acea bucată reală de date din memorie ale cărei dimensiune, model, caracteristici și (într-o oarecare măsură) comportament sunt definite de definiția clasei. Modul obișnuit de a crea obiecte este apelarea unui constructor (clasele pot avea, în general, constructori separați). De exemplu,
elev de clasă { Student(String studentName, String Address, int ID) { // ... aici stocăm datele de intrare și alte câmpuri interne... } // ... }