Constructor (programare orientată pe obiecte)

Versiunea actuală a paginii nu a fost încă revizuită de colaboratori experimentați și poate diferi semnificativ de versiunea revizuită pe 28 iunie 2016; verificările necesită 22 de modificări .

Î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.

Atribuirea constructorului

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 ) ;

Tipuri de constructori

O varietate de limbaje de programare prezintă mai multe varietăți de constructori:

  • constructor cu parametri;
  • constructor implicit care nu ia argumente;
  • named constructor - o funcție care presupune un apel explicit după nume care funcționează ca un constructor
  • copy constructor  - un constructor care ia ca argument un obiect din aceeași clasă (sau o referință de la acesta);
  • constructor de conversie - un constructor care ia un singur argument (acești constructori pot fi apelați automat pentru a converti valori de alte tipuri în obiecte din această clasă).
  • constructor de mutare ( specific C++11 )
clasa Complex { public : // Constructor implicit // (în acest caz și un constructor de conversie) Complex ( double i_re = 0 , double i_im = 0 ) : re ( i_re ), im ( i_im ) {} // Constructor de copiere complexă ( const Complex & obj ) { re = obj . re ; im = obj . sunt ; } privat : dublu re , im ; };

Constructor cu parametri

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 implicit

Constructor implicit

Un 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ă).

Constructor numit

Copiați constructorul

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.

Constructor de conversie

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).

Constructorul de mutare

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.

Constructor virtual

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:

  • această clasă este un descendent al unei clase predefinite (în acest exemplu, este o clasă TVehicle);
  • pe întreaga cale de moștenire de la clasa de bază la cea creată, lanțul de redefinire nu s-a rupt. La suprascrierea unei metode virtuale, sintaxa Delphi necesită cuvântul cheie, overloadastfel încât funcțiile vechi și noi cu semnături diferite să poată coexista, overridefie pentru a suprascrie funcția, fie reintroducepentru a defini o nouă funcție cu același nume - aceasta din urmă nu este permisă.
tip TVehicle = constructor de clasă Create ; virtual ; sfârşitul ; TAutomobile = constructor de clasă ( TVehicle ) Creare ; suprascrie ; sfârşitul ; TMotorcycle = constructor de clasă ( TVehicle ) Creare ; suprascrie ; sfârşitul ; TMoped = clasa ( TMotorcycle ) // întrerupe lanțul de redefinire - începe unul nou Creare constructor Creare ( x : întreg ) ; reintroduce ; sfârşitul ;

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)

Sintaxă

C++

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 ; };

Python

Î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.""" trece

Ruby

Limbajul 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

Delphi

Î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 ;

Java

Câteva diferențe între constructori și alte metode Java :

  • constructorii nu au un tip de returnare (de fapt, returnează întotdeauna acest lucru);
  • constructorii nu pot fi apelați direct (trebuie folosit cuvântul cheie new);
  • constructorii nu pot avea synchronized, final, abstractși modificatori native;static
Exemplu clasă publică Exemplu { private int date ; // Constructor implicit, datele sunt inițializate la 1 când este creată o instanță a clasei Exemplu public Exemplu () { data = 1 ; } // Constructor overload public Exemplu ( int input ) { data = input ; } } // cod care ilustrează crearea unui obiect de către constructorul descris mai sus Exemplu e = nou Exemplu ( 42 );

JavaScript

Î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 );

Visual Basic .NET

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

C#

Exemplu clasa MyClass { private int _number ; șir privat _șir ; public MyClass ( int num , string str ) { _number = num ; _string = str ; } } // Cod care ilustrează crearea unui obiect de către constructorul descris mai sus Exemplu MyClass = new MyClass ( 42 , "string" );

Eiffel

Î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:

  • Procedurile de creare nu au niciun tip de rezultat returnat explicit (așa cum este definit de procedura [Nota 1] ).
  • procedurile de creare sunt denumite (numele sunt limitate la identificatori validi);
  • procedurile de creare sunt specificate prin nume în textul clasei;
  • procedurile de creare pot fi apelate direct (ca și procedurile normale) pentru a reinițializa obiectele;
  • fiecare clasă efectivă (adică concretă, nu abstractă) trebuie să specifice (explicit sau implicit) cel puțin o procedură de creare;
  • procedurile de creare sunt responsabile pentru aducerea obiectului nou inițializat într-o stare care satisface invariantul clasei [Nota 2] .

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:

  • creați o nouă instanță directă de tipul T[Nota 4] ;
  • execută procedura de creare makepentru instanța nou creată;
  • atașați obiectul nou creat la entitate x.
Exemplu

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 ) ...

Cold Fusion

Exemplu

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>

PHP

Exemplu

Î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 ; } }

Perl

Exemplu

Î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.

Constructori simplificați (cu pseudocod )

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... } // ... }

Vezi și

Note

  1. Subrutinele Eiffel sunt fie proceduri, fie funcții . Procedurile nu au niciun tip de returnare. Funcțiile au întotdeauna un tip de returnare.
  2. Deoarece invariantul clasei (claselor) moștenite trebuie de asemenea să fie satisfăcut, nu există nicio cerință obligatorie de a apela constructorii părinte.
  3. Specificația completă este conținută în standardele ISO/ECMA pentru limbajul de programare Eiffel, disponibile online. [2]
  4. Standardul Eiffel cere ca câmpurile să fie inițializate prima dată când sunt accesate, inclusiv. nu este nevoie să le inițializați cu valori implicite la momentul creării obiectului.

Link -uri

  1. Desigur, acest lucru duce la anumite dificultăți tehnice - de exemplu, ce se întâmplă dacă se aruncă o excepție de la constructor ? Cu toate acestea, dezvoltatorul clasei trebuie pur și simplu să respecte cerințele limbajului, iar majoritatea programelor nu necesită diagnostice detaliate și reîncercări automate asupra erorilor.
  2. Document de descriere ISO/ECMA Eiffel . Consultat la 19 aprilie 2009. Arhivat din original pe 16 iunie 2008.